Skip to content
2 changes: 1 addition & 1 deletion hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,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
14 changes: 11 additions & 3 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,17 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case bs: BlockMemberSymbol =>
disamb.flatMap(_.defn) match
case S(_) if bs.asCls.exists(_ is ctx.builtins.Int31) =>
return term(Sel(State.runtimeSymbol.ref().resolve, ref.tree)(S(bs), N).withLocOf(ref).resolve)(k)


// return term(Sel(State.runtimeSymbol.ref().resolve, ref.tree)(S(bs), N).withLocOf(ref).resolve)(k)


return term(Sel(State.runtimeSymbol.ref().resolve, ref.tree)(S(bs), N, N).withLocOf(ref).resolve)(k)
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, which might seem more appealing,
// * works but 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 @@ -752,7 +760,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
msg"Cannot compile ${t.describe} term that was not elaborated (maybe elaboration was one in 'lightweight' mode?)" ->
t.toLoc :: Nil,
source = Diagnostic.Source.Compilation)
case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted
case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel
=> fail:
ErrorReport(
msg"Unexpected term form in expression position (${t.describe})" ->
Expand Down
38 changes: 26 additions & 12 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Keyword.{`let`, `set`}
object Elaborator:

val binaryOps = Set(
",",
",", // * Not currently used directly; but `;` (below) maps to it
"+", "-", "*", "/", "%",
"==", "!=", "<", "<=", ">", ">=",
"===", "!==",
Expand Down 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 @@ -259,7 +271,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 @@ -434,10 +446,10 @@ extends Importer with ucs.SplitElaborator:
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 @@ -577,6 +589,8 @@ extends Importer with ucs.SplitElaborator:
val preTrm = subterm(pre)
val sym = resolveField(nme, preTrm.symbol, nme)
Term.SynthSel(preTrm, nme)(sym, N)
case Sel(Empty(), nme) =>
Term.LeadingDotSel(nme)(S(summon)).withLocOf(tree)
case Sel(pre, nme) =>
val preTrm = subterm(pre)
val sym = resolveField(nme, preTrm.symbol, nme)
Expand All @@ -603,7 +617,7 @@ extends Importer with ucs.SplitElaborator:
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 @@ -697,7 +711,7 @@ extends Importer with ucs.SplitElaborator:
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 @@ -1135,7 +1149,7 @@ extends Importer with ucs.SplitElaborator:
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)
mtdSym.tsym = S(tsym)
val htd = HandlerTermDefinition(resumeSym, td)
Expand All @@ -1152,7 +1166,7 @@ extends Importer with ucs.SplitElaborator:
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).withLocOf(td)
tsym.defn = S(tdf)
sym.tsym = S(tsym)
Expand Down Expand Up @@ -1187,6 +1201,8 @@ extends Importer with ucs.SplitElaborator:

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 @@ -1242,7 +1258,6 @@ extends Importer with ucs.SplitElaborator:
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 @@ -1362,7 +1377,7 @@ extends Importer with ucs.SplitElaborator:
val md =
val (bod, c) = mkBody
ModuleOrObjectDef(owner, modSym, sym,
tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)
tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx)
modSym.defn = S(md)
md
case Cls =>
Expand All @@ -1386,7 +1401,6 @@ extends Importer with ucs.SplitElaborator:
S(tps.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))),
S(clsSym.ref()),
N,
FlowSymbol(s"‹constructor›"),
TermDefFlags.empty,
Modulefulness.none,
annotations.collect:
Expand Down Expand Up @@ -1706,7 +1720,7 @@ extends Importer with ucs.SplitElaborator:
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
12 changes: 10 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ class Resolver(tl: TraceLogger)
defs.foreach(d => traverseDefn(d.td))
traverse(body, expect = NonModule(N))

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)

Expand Down Expand Up @@ -356,8 +360,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 @@ -574,6 +577,8 @@ class Resolver(tl: TraceLogger)
resolveType(t, prefer = prefer)
(t.callableDefn, ictx)

case Term.LeadingDotSel(nme) => (N, ictx)

case Term.Ref(_: BlockMemberSymbol) =>
resolveSymbol(t, prefer = prefer, sign = false)
resolveType(t, prefer = prefer)
Expand Down Expand Up @@ -1007,6 +1012,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
69 changes: 54 additions & 15 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,18 @@ 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 =
cfg.shownSymbols += this
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 @@ -99,7 +111,7 @@ abstract class Symbol(using State) extends Located:
case mem: BlockMemberSymbol => S(mem)
case mem: DefinitionSymbol[?] => 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
case _ => N

Expand Down Expand Up @@ -138,10 +150,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 @@ -153,10 +165,14 @@ object FlowSymbol:

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

def sel(nme: Str)(using State) =
FlowSymbol(s"⋅$nme")

def lds(nme: Str)(using State) =
FlowSymbol(s"Ɛ⋅$nme")

end FlowSymbol

Expand Down Expand Up @@ -203,6 +219,23 @@ class BuiltinSymbol

def subst(using sub: SymbolSubst): BuiltinSymbol = sub.mapBuiltInSym(this)

lazy val signature : semantics.flow.Producer =
import typing.Type
import typing.Type.*
val binaryType : Type = Fun(args = Ls(Top, Top), ret = Top, eff = N)
val unaryType : Type = Fun(args = Ls(Top), ret = Top, eff = N)
val nullaryType : Type = Top
val typ = (binary, unary, nullary) match
case (true, true, true) => Union(binaryType, Union(unaryType, nullaryType))
case (true, true, _) => Union(binaryType, unaryType)
case (true, _, true) => Union(binaryType, nullaryType)
case (_, true, true) => Union(unaryType, nullaryType)
case (true, _, _) => binaryType
case (_, true, _) => unaryType
case (_, _, true) => nullaryType
case _ => Bot
semantics.flow.Producer.Typ(typ)


/** This is the outside-facing symbol associated to a possibly-overloaded
* definition living in a block – e.g., a module or class.
Expand Down Expand Up @@ -243,13 +276,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"flow:$nme")(using getState)

end BlockMemberSymbol


sealed abstract class MemberSymbol(using State) extends Symbol:
def nme: Str

// var defn: Opt[Defn] = N // FIXME still needed?
// def bms: Opt[BlockMemberSymbol] = defn.map(_.bsym) // FIXME still needed?
def subst(using SymbolSubst): MemberSymbol


Expand All @@ -272,21 +309,22 @@ object TermSymbol:


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 @@ -324,6 +362,7 @@ sealed trait DefinitionSymbol[Defn <: Definition] extends Symbol:
this: MemberSymbol =>

var defn: Opt[Defn] = N
def bms: Opt[BlockMemberSymbol] = defn.map(_.bsym)

def subst(using sub: SymbolSubst): DefinitionSymbol[Defn]

Expand All @@ -339,10 +378,10 @@ sealed trait InnerSymbol(using State) extends Symbol:
// parameter to all occurrences of InnerSymbol. So, we use a self-type annotation instead to
// ensure that any implementation of InnerSymbol is also a DefinitionSymbol.
self: DefinitionSymbol[?] =>

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