Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ThisBuild / scalacOptions ++= Seq(
"-feature",
"-unchecked",
"-language:higherKinds",
"-language:implicitConversions",
if (insideCI.value) "-Wconf:any:error"
else "-Wconf:any:warning",
)
Expand Down
6 changes: 4 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class MLsCompiler(preludeFile: os.Path, mkOutput: ((Str => Unit) => Unit) => Uni
val resolver = Resolver(rtl)
resolver.traverseBlock(blk0)(using Resolver.ICtx.empty)
val blk = new semantics.Term.Blk(
semantics.Import(State.runtimeSymbol, runtimeFile.toString) :: semantics.Import(State.termSymbol, termFile.toString) :: blk0.stats,
semantics.Import(State.runtimeSymbol, runtimeFile.toString, runtimeFile)
:: semantics.Import(State.termSymbol, termFile.toString, termFile)
:: blk0.stats,
blk0.res
)
val low = ltl.givenIn:
Expand All @@ -95,7 +97,7 @@ class MLsCompiler(preludeFile: os.Path, mkOutput: ((Str => Unit) => Unit) => Uni
codegen.js.JSBuilder()
val le = low.program(blk)
val baseScp: utils.Scope =
utils.Scope.empty
utils.Scope.empty(utils.Scope.Cfg.default)
// * This line serves for `import.meta.url`, which retrieves directory and file names of mjs files.
// * Having `module id"import" with ...` in `prelude.mls` will generate `globalThis.import` that is undefined.
baseScp.addToBindings(Elaborator.State.importSymbol, "import", shadow = false)
Expand Down
2 changes: 1 addition & 1 deletion hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
goStats(stats)
case (modDef: ModuleOrObjectDef) :: stats =>
goStats(stats)
case Import(sym, pth) :: stats =>
case Import(sym, str, pth) :: stats =>
goStats(stats) // TODO:
case stat :: _ =>
TODO(stat)
Expand Down
8 changes: 5 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case DefineVar(sym, rhs) :: stats =>
term(rhs): r =>
Assign(sym, r, blockImpl(stats, res)(k))
case (imp @ Import(sym, path)) :: stats =>
case (imp: Import) :: stats =>
raise(ErrorReport(
msg"Imports must be at the top level" ->
imp.toLoc :: Nil,
Expand Down Expand Up @@ -369,7 +369,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case bs: BlockMemberSymbol =>
bs.defn match
case S(d) if d.hasDeclareModifier.isDefined =>
return term(Sel(State.globalThisSymbol.ref().resolve, ref.tree)(S(bs), N).withLocOf(ref).resolve)(k)
return term(Sel(State.globalThisSymbol.ref().resolve, ref.tree)(S(bs), N, N).withLocOf(ref).resolve)(k)
// * Note: the alternative below does not instrument the selection to check for `undefined`!
// return k(Value.Ref(State.globalThisSymbol).sel(ref.tree, bs).withLocOf(ref))
case S(td: TermDefinition) if td.k is syntax.Fun =>
// * Local functions with no parameter lists are getters
// * and are lowered to functions with an empty parameter list
Expand Down Expand Up @@ -1029,7 +1031,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
val res = MergeMatchArmTransformer.applyBlock(lifted)

Program(
imps.map(imp => imp.sym -> imp.file),
imps.map(imp => imp.sym -> imp.str),
res
)

Expand Down
33 changes: 23 additions & 10 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ object Elaborator:
case _: OuterCtx.LocalScope =>
parent.fold(ReturnHandler.NotInFunction)(_.getRetHandler)

lazy val outermostAcessibleBase: (Ctx, Ls[InnerSymbol]) =
import OuterCtx.*
outer match
case InnerScope(inner) =>
parent match
case N => (this, inner :: Nil)
case S(par) =>
val (base, path) = par.outermostAcessibleBase
(base, inner :: path)
case _: (Function | LocalScope) | LambdaOrHandlerBlock | NonReturnContext =>
(this, Nil)

// * Invariant: We expect that the top-level context only contain hard-coded symbols like `globalThis`
// * and that built-in symbols like Int and Str be imported into another nested context on top of it.
// * It should not be possible to shadow these built-in symbols, so user code should always be compiled
Expand Down Expand Up @@ -230,7 +242,7 @@ object Elaborator:
val bsym = BlockMemberSymbol("ret", Nil, true)
val defn = ClassDef(N, syntax.Cls, sym, bsym, Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), Nil, N)
sym.defn = S(defn)
Term.Sel(runtimeSymbol.ref(), id)(S(sym), N)
Term.Sel(runtimeSymbol.ref(), id)(S(sym), N, N)
val nonLocalRet =
val id = new Ident("ret")
BlockMemberSymbol(id.name, Nil, true)
Expand Down Expand Up @@ -390,10 +402,10 @@ extends Importer:
case trm => raise(WarningReport(msg"Terms in handler block do nothing" -> trm.toLoc :: Nil))

val tds = elabed.stats.map {
case td @ TermDefinition(Fun, sym, tsym, params, tparams, sign, body, resSym, flags, mf, annotations, comp) =>
case td @ TermDefinition(Fun, sym, tsym, params, tparams, sign, body, flags, mf, annotations, comp) =>
params.reverse match
case ParamList(_, value :: Nil, _) :: newParams =>
val newTd = TermDefinition(Fun, sym, tsym, newParams.reverse, tparams, sign, body, resSym, flags, mf, annotations, comp)
val newTd = TermDefinition(Fun, sym, tsym, newParams.reverse, tparams, sign, body, flags, mf, annotations, comp)
S(HandlerTermDefinition(value.sym, newTd))
case _ =>
raise(ErrorReport(msg"Handler function is missing resumption parameter" -> td.toLoc :: Nil))
Expand Down Expand Up @@ -562,7 +574,7 @@ extends Importer:
val loc = tree.toLoc.getOrElse(???)
Term.Lit(StrLit(loc.origin.fileName.toString))
else
Term.Sel(preTrm, nme)(sym, N)
Term.Sel(preTrm, nme)(sym, N, S(summon))
case MemberProj(ct, nme) =>
val c = subterm(ct)
val f = c.symbol.flatMap(_.asCls) match
Expand Down Expand Up @@ -663,7 +675,7 @@ extends Importer:
val argTree = new Tup(body :: Nil)
val dummyIdent = new Ident("return").withLocOf(kw)
Term.App(
Term.Sel(sym.ref(dummyIdent), retMtdTree)(S(state.nonLocalRet), N),
Term.Sel(sym.ref(dummyIdent), retMtdTree)(S(state.nonLocalRet), N, S(summon)),
Term.Tup(PlainFld(subterm(body)) :: Nil)(argTree)
)(App(Sel(dummyIdent, retMtdTree), argTree), N, rs)
case ReturnHandler.NotInFunction =>
Expand Down Expand Up @@ -1091,7 +1103,7 @@ extends Importer:
val tsym = TermSymbol(Fun, N, Ident("ret"))
val td = TermDefinition(
Fun, mtdSym, tsym, PlainParamList(Param(FldFlags.empty, valueSym, N, Modulefulness.none) :: Nil) :: Nil,
N, N, S(valueSym.ref(Ident("value"))), FlowSymbol(s"‹result of non-local return›"), TermDefFlags.empty, Modulefulness.none, Nil, N)
N, N, S(valueSym.ref(Ident("value"))), TermDefFlags.empty, Modulefulness.none, Nil, N)
tsym.defn = S(td)
val htd = HandlerTermDefinition(resumeSym, td)
Term.Handle(nonLocalRetHandler, state.nonLocalRetHandlerTrm, Nil, clsSym, htd :: Nil, b)
Expand All @@ -1107,7 +1119,7 @@ extends Importer:
Modulefulness.none

val tsym = TermSymbol(k, owner, id) // TODO?
val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body, r,
val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body,
TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N)
tsym.defn = S(tdf)
sym.defn = S(tdf)
Expand All @@ -1133,6 +1145,8 @@ extends Importer:
return go(sts, Nil, acc)
val sym = members.getOrElse(nme.name, lastWords(s"Symbol not found: ${nme.name}"))

val outerCtx = ctx

var newCtx = S(td.symbol).collectFirst:
case s: InnerSymbol => s
.fold(ctx.nest(OuterCtx.NonReturnContext))(ctx.nestInner(_))
Expand Down Expand Up @@ -1188,7 +1202,6 @@ extends Importer:
tsym,
Nil, N, N,
S(p.sym.ref()),
FlowSymbol("‹class-param-res›"),
TermDefFlags.empty.copy(isMethod = (k is Cls)),
p.modulefulness,
Nil,
Expand Down Expand Up @@ -1304,7 +1317,7 @@ extends Importer:
log(s"Companion: ${comp}")
val md =
val (bod, c) = mkBody
ModuleOrObjectDef(owner, modSym, sym,
ModuleOrObjectDef(outerCtx, owner, modSym, sym,
tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)
modSym.defn = S(md)
md
Expand Down Expand Up @@ -1574,7 +1587,7 @@ extends Importer:
def computeVariances(s: Statement): Unit =
val trav = VarianceTraverser()
def go(s: Statement): Unit = s match
case TermDefinition(k, sym, tsym, pss, _, sign, body, r, _, _, _, _) =>
case TermDefinition(k, sym, tsym, pss, _, sign, body, r, _, _, _) =>
pss.foreach(ps => ps.params.foreach(trav.traverseType(S(false))))
sign.foreach(trav.traverseType(S(true)))
body match
Expand Down
8 changes: 4 additions & 4 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class Importer:
file.ext match

case "mjs" | "js" =>
Import(sym, file.toString)
Import(sym, file.toString, file)

case "mls" =>

Expand Down Expand Up @@ -69,13 +69,13 @@ class Importer:
case None => lastWords(s"File $file does not define a symbol named $nme")

val jsFile = file / os.up / (file.baseName + ".mjs")
Import(sym, jsFile.toString)
Import(sym, jsFile.toString, jsFile)

case _ =>
raise(ErrorReport(msg"Unsupported file extension: ${file.ext}" -> N :: Nil))
Import(sym, file.toString)
Import(sym, path, file)

else
Import(sym, path)
Import(sym, path, file)


10 changes: 8 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ class Resolver(tl: TraceLogger)
args.foreach(traverse(_, expect = NonModule(N)))
rft.foreach((sym, bdy) => traverseBlock(bdy.blk))

case t: Term.Lam =>
t.params.foreach(traverseParam)
traverse(t.body, expect = NonModule(N))

case t: Resolvable =>
resolve(t, prefer = expect, inAppPrefix = false, inTyPrefix = false, inCtxPrefix = false)
t.expanded match
Expand Down Expand Up @@ -370,8 +374,7 @@ class Resolver(tl: TraceLogger)
trace(s"Resolving definition: $defn"):
def traverseTermDef(tdf: TermDefinition) =
val TermDefinition(_k, _sym, _tsym,
pss, tps, sign, body,
_resSym, TermDefFlags(isMethod), modulefulness, annotations, comp
pss, tps, sign, body, TermDefFlags(isMethod), modulefulness, annotations, comp
) = tdf
/**
* Add the contextual parameters in pss to the ICtx so that they
Expand Down Expand Up @@ -894,6 +897,9 @@ class Resolver(tl: TraceLogger)

def traverseParam(p: Param)(using ictx: ICtx): Unit =
log(s"Resolving parameter ${p.showDbg}")
val ty = p.sign.map(sign =>
resolveSign(sign, expect = if p.modulefulness.modified then Module(N) else NonModule(N)))
p.signType = ty
if p.modulefulness.modified then
if p.sign.isEmpty then
raise(ErrorReport(msg"Module parameter must have explicit type." -> p.sym.toLoc :: Nil))
Expand Down
44 changes: 31 additions & 13 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ abstract class Symbol(using State) extends Located:

val uid: Uid[Symbol] = State.suid.nextUid

def getName(using scp: Scope): hkmc2.document.Document =
import hkmc2.document.*
scp.allocateOrGetName(this)

def showName(using scp: Scope, cfg: ShowCfg): hkmc2.document.Document =
import hkmc2.document.*
val name = nme
if cfg.showFlowSymbols
then s"$name${scp.allocateOrGetName(this).stripPrefix(name)}"
else name

val directRefs: mutable.Buffer[Term.Ref] = mutable.Buffer.empty
def ref(id: Tree.Ident =
Tree.Ident("") // FIXME hack
Expand Down Expand Up @@ -98,7 +109,7 @@ abstract class Symbol(using State) extends Located:
case mem: BlockMemberSymbol => S(mem)
case mem: MemberSymbol[?] => mem.defn match
case S(defn: TypeLikeDef) => S(defn.bsym)
case S(defn: TermDefinition) => S(defn.sym)
case S(defn: TermDefinition) => S(defn.bsym)
case N => N

/** Get the symbol corresponding to the "representative" of a set of overloaded definitions,
Expand All @@ -124,10 +135,10 @@ end Symbol
class FlowSymbol(label: Str)(using State) extends Symbol:
def nme: Str = label
def toLoc: Option[Loc] = N // TODO track source trees of flows
import typing.*
import flow.*
val outFlows: mutable.Buffer[FlowSymbol] = mutable.Buffer.empty
val outFlows2: mutable.Buffer[Consumer] = mutable.Buffer.empty
val inFlows: mutable.Buffer[ConcreteProd] = mutable.Buffer.empty
val consumers: mutable.Buffer[Consumer] = mutable.Buffer.empty
val producers: mutable.Buffer[ConcreteProd] = mutable.Buffer.empty
def showDbg: Str =
label + s"‹$uid›"
override def toString: Str =
Expand All @@ -139,7 +150,8 @@ object FlowSymbol:

def app()(using State) =
// FlowSymbol("‹app-res›")
FlowSymbol("@")
// FlowSymbol("@")
FlowSymbol("app")

def sel(nme: Str)(using State) =
FlowSymbol(s"⋅$nme")
Expand Down Expand Up @@ -229,13 +241,17 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[TypeOrTermDef], val nameIsMe
s"member:$nme${State.dbgUid(uid)}"

def subst(using sub: SymbolSubst): BlockMemberSymbol = sub.mapBlockMemberSym(this)


// * The flow of this symbol, when interpreted as a term (assuming no disambiguation)
lazy val flow: FlowSymbol = FlowSymbol(s"member-flow:$nme")(using getState)

end BlockMemberSymbol


sealed abstract class MemberSymbol[Defn <: Definition](using State) extends Symbol:
def nme: Str
var defn: Opt[Defn] = N
def bms: Opt[BlockMemberSymbol] = defn.map(_.bsym)
def subst(using SymbolSubst): MemberSymbol[Defn]


Expand All @@ -250,21 +266,22 @@ class TermSymbol(val k: TermDefKind, val owner: Opt[InnerSymbol], val id: Tree.I


sealed trait CtorSymbol extends Symbol:
def nme: Str
def subst(using sub: SymbolSubst): CtorSymbol = sub.mapCtorSym(this)

case class Extr(isTop: Bool)(using State) extends CtorSymbol:
def nme: Str = if isTop then "Top" else "Bot"
def toLoc: Option[Loc] = N
override def toString: Str = nme

case class LitSymbol(lit: Literal)(using State) extends CtorSymbol:
def nme: Str = lit.toString
sealed abstract case class LitSymbol(lit: Literal)(using State) extends CtorSymbol:
def nme: Str = lit.idStr
def toLoc: Option[Loc] = lit.toLoc
override def toString: Str = s"lit:$lit"
case class TupSymbol(arity: Opt[Int])(using State) extends CtorSymbol:
def nme: Str = s"Tuple#$arity"
def toLoc: Option[Loc] = N
override def toString: Str = s"tup:$arity"
object LitSymbol:
val cache: mutable.Map[Literal, LitSymbol] = mutable.Map.empty
def apply(lit: Literal)(using State): LitSymbol =
cache.getOrElseUpdate(lit, new LitSymbol(lit){})


/** A TypeSymbol that is not an alias. */
Expand Down Expand Up @@ -298,9 +315,10 @@ sealed trait ClassLikeSymbol extends IdentifiedSymbol:
* A `Ref(_: InnerSymbol)` represents a `this`-like reference to the current object. */
// TODO prevent from appearing in Ref
sealed trait InnerSymbol(using State) extends Symbol:
val privatesScope: Scope = Scope.empty // * Scope for private members of this symbol
val privatesScope: Scope = Scope.empty(Scope.Cfg.default) // * Scope for private members of this symbol
val thisProxy: TempSymbol = TempSymbol(N, s"this$$$nme")
def subst(using SymbolSubst): InnerSymbol
def bms: Opt[BlockMemberSymbol]

trait IdentifiedSymbol extends Symbol:
val id: Tree.Ident
Expand Down
Loading
Loading