From 156d657f1a216c9b03d1c234d5b78778971c35ba Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 15 Jan 2026 15:55:58 +0800 Subject: [PATCH 01/18] very lazy rebase --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 858 ++++++++++++------ .../main/scala/hkmc2/codegen/Lowering.scala | 5 +- .../main/scala/hkmc2/codegen/ScopeData.scala | 314 +++++++ .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 612 ++++++------- 4 files changed, 1182 insertions(+), 607 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index fc6fa76683..e6aedfa4f3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -8,6 +8,7 @@ import utils.* import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.Message.* +import hkmc2.ScopeData.* import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt @@ -15,8 +16,10 @@ import hkmc2.codegen.llir.FreshInt import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet +import scala.collection.mutable.ListBuffer object Lifter: + /** * Describes the free variables of a function that have been accessed by its nested definitions. * @param vars The free variables that are accessed by nested classes/functions. @@ -50,7 +53,7 @@ object Lifter: case class AccessInfo( accessed: Set[Local], mutated: Set[Local], - refdDefns: Set[BlockMemberSymbol] + refdDefns: Set[ScopedInfo] ): def ++(that: AccessInfo) = AccessInfo( accessed ++ that.accessed, @@ -67,19 +70,9 @@ object Lifter: mutated.intersect(locals), refdDefns ) - def withoutBms(locals: Set[BlockMemberSymbol]) = AccessInfo( - accessed, - mutated, - refdDefns -- locals - ) - def intersectBms(locals: Set[BlockMemberSymbol]) = AccessInfo( - accessed, - mutated, - refdDefns.intersect(locals) - ) def addAccess(l: Local) = copy(accessed = accessed + l) def addMutated(l: Local) = copy(accessed = accessed + l, mutated = mutated + l) - def addRefdDefn(l: BlockMemberSymbol) = copy(refdDefns = refdDefns + l) + def addRefdScopedObj(l: ScopedInfo) = copy(refdDefns = refdDefns + l) object AccessInfo: val empty = AccessInfo(Set.empty, Set.empty, Set.empty) @@ -97,10 +90,6 @@ object Lifter: case _ => Set.empty - def getVarsBlk(b: Block): Set[Local] = - b.definedVars.collect: - case s: LocalVarSymbol => s - object RefOfBms: def unapply(p: Path): Opt[(BlockMemberSymbol, Opt[DefinitionSymbol[?]])] = p match case Value.Ref(l: BlockMemberSymbol, disamb) => S((l, disamb)) @@ -119,12 +108,11 @@ object Lifter: case c: ClsLikeDefn => (c.companion.isDefined) || (c.k is syntax.Obj) // TODO: refine handling of companions case _ => false - /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. */ -class Lifter()(using State, Raise, Config): +class Lifter(blk: Block)(using State, Raise): import Lifter.* /** @@ -247,122 +235,6 @@ class Lifter()(using State, Raise, Config): case Sym(l) => Value.Ref(l, d) case PubField(isym, sym) => Select(isym.asPath, Tree.Ident(sym.nme))(d) - - - val ignoredSet = Set(State.runtimeSymbol.asPath.selSN("NonLocalReturn")) - - def isIgnoredPath(p: Path) = ignoredSet.contains(p) - - /** - * Creates a capture class for a function consisting of its mutable (and possibly immutable) local variables. - * @param f The function to create the capture class for. - * @param ctx The lifter context. Determines which variables will be captured. - * @return The triple (defn, varsMap, varsList), where `defn` is the capture class's definition, - * `varsMap` maps the function's locals to the corresponding `VarSymbol` (for the class parameters), and - * `varsList` specifies the order of these variables in the class's constructor. - */ - def createCaptureCls(f: FunDefn, ctx: LifterCtx) - : (ClsLikeDefn, Map[Local, VarSymbol], List[Local]) = - val nme = f.sym.nme + "$capture" - - val clsSym = ClassSymbol( - Tree.DummyTypeDef(syntax.Cls), - Tree.Ident(nme) - ) - - val FreeVars(_, cap) = ctx.usedLocals(f.sym) - - val fresh = FreshInt() - - val sortedVars = cap.toArray.sortBy(_.uid).map: sym => - val id = fresh.make - val nme = sym.nme + "$capture$" + id - - val ident = new Tree.Ident(nme) - val varSym = VarSymbol(ident) - val fldSym = BlockMemberSymbol(nme, Nil) - val tSym = TermSymbol(syntax.MutVal, S(clsSym), ident) - - val p = Param(FldFlags.empty.copy(isVal = true), varSym, N, Modulefulness.none) - varSym.decl = S(p) // * Currently this is only accessed to create the class' toString method - - val vd = ValDefn( - tSym, - fldSym, - Value.Ref(varSym) - ) - - (sym -> varSym, p, vd) - - val defn = ClsLikeDefn( - None, clsSym, BlockMemberSymbol(nme, Nil), - S(TermSymbol(syntax.Fun, S(clsSym), clsSym.id)), - syntax.Cls, - N, - PlainParamList(sortedVars.iterator.map(_._2).toList) :: Nil, None, Nil, Nil, - Nil, - End(), - sortedVars.iterator.foldLeft[Block](End()): - case (acc, (_, _, vd)) => Define(vd, acc), - N, - N, - ) - - (defn, sortedVars.iterator.map(_._1).toMap, sortedVars.iterator.map(_._1._1).toList) - - private val innerSymCache: MutMap[Local, Set[Local]] = MutMap.empty - - /** - * Gets the inner symbols referenced within a class (including those within a member symbol). - * @param c The class from which to get the inner symbols. - * @return The inner symbols reference within a class. - */ - def getInnerSymbols(c: Defn) = - val sym = c match - case f: FunDefn => f.sym - case c: ClsLikeDefn => c.isym - case _ => wat("unreachable", c.sym) - - def create: Set[Local] = c.freeVars.collect: - case s: InnerSymbol => s - case t: TermSymbol if t.owner.isDefined => t.owner.get - - innerSymCache.getOrElseUpdate(sym, create) - - /** - * Determines whether a certain class's `this` needs to be captured by a class being lifted. - * @param captureCls The class in question that is considered for capture. - * @param liftDefn The class being lifted. - * @return Whether the class needs to be captured. - */ - private def needsClsCapture(captureCls: ClsLikeDefn, liftDefn: Defn) = - getInnerSymbols(liftDefn).contains(captureCls.isym) - - /** - * Determines whether a certain function's mutable closure needs to be captured by a definition being lifted. - * @param captureFn The function in question that is considered for capture. - * @param liftDefn The definition being lifted. - * @return Whether the function needs to be captured. - */ - private def needsCapture(captureFn: FunDefn, liftDefn: Defn, ctx: LifterCtx) = - val candVars = liftDefn.freeVars - val captureFnVars = ctx.usedLocals(captureFn.sym).reqCapture.toSet - !candVars.intersect(captureFnVars).isEmpty - - /** - * Gets the immutable local variables of a function that need to be captured by a definition being lifted. - * @param captureFn The function in question whose local variables need to be captured. - * @param liftDefn The definition being lifted. - * @return The local variables that need to be captured. - */ - private def neededImutLocals(captureFn: FunDefn, liftDefn: Defn, ctx: LifterCtx) = - val candVars = liftDefn.freeVars - val captureFnVars = ctx.usedLocals(captureFn.sym) - val mutVars = captureFnVars.reqCapture.toSet - val imutVars = captureFnVars.vars - imutVars.filter: s => - !mutVars.contains(s) && candVars.contains(s) - case class FunSyms[T <: DefinitionSymbol[?]](b: BlockMemberSymbol, d: T): def asPath = Value.Ref(b, S(d)) object FunSyms: @@ -383,82 +255,59 @@ class Lifter()(using State, Raise, Config): val liftedDefn: T, val extraDefns: List[Defn], ) - - private case class LifterMetadata( - unliftable: Set[BlockMemberSymbol], - modules: List[ClsLikeDefn], - objects: List[ClsLikeDefn], - firstClsFns: Set[BlockMemberSymbol] - ) + + type ClsLikeSym = DefinitionSymbol[? <: ClassDef | ModuleOrObjectDef] + type ClsSym = DefinitionSymbol[? <: ClassLikeDef] + type ModuleOrObjSym = DefinitionSymbol[? <: ModuleOrObjectDef] + + case class LifterMetadata( + unliftable: Set[ClsSym | ModuleOrObjSym], + modules: Set[ModuleOrObjSym], + firstClsFns: Set[TermSymbol] + ): + def ++(that: LifterMetadata) = + LifterMetadata(unliftable ++ that.unliftable, modules ++ that.modules, firstClsFns ++ that.firstClsFns) + object LifterMetadata: + def empty = LifterMetadata(Set.empty, Set.empty, Set.empty) // d is a top-level definition // returns (ignored classes, modules, objects) - private def createMetadata(d: Defn, ctx: LifterCtx): LifterMetadata = - var ignored: Set[BlockMemberSymbol] = Set.empty - var firstClsFns: Set[BlockMemberSymbol] = Set.empty - var unliftable: Set[BlockMemberSymbol] = Set.empty - var clsSymToBms: Map[Local, BlockMemberSymbol] = Map.empty - var modules: List[ClsLikeDefn] = Nil - var objects: List[ClsLikeDefn] = Nil - var extendsGraph: Set[(BlockMemberSymbol, BlockMemberSymbol)] = Set.empty - - d match - case c @ ClsLikeDefn(k = syntax.Mod) => modules ::= c - case c @ ClsLikeDefn(k = syntax.Obj) => objects ::= c - case _ => () - - // search for modules - new BlockTraverser: - applyDefn(d) - override def applyDefn(defn: Defn): Unit = - if defn === d then - super.applyDefn(defn) - else - defn match - case c: ClsLikeDefn => - clsSymToBms += c.isym -> c.sym - - if c.companion.isDefined then // TODO: refine handling of companions - raise(WarningReport( - msg"Modules are not yet lifted." -> N :: Nil, - N, Diagnostic.Source.Compilation - )) - modules ::= c - ignored += c.sym - else if c.k is syntax.Obj then - objects ::= c - case _ => () - super.applyDefn(defn) + private def createMetadata(s: ScopeNode): LifterMetadata = + var ignored: Set[ClsSym | ModuleOrObjSym] = Set.empty + var firstClsFns: Set[TermSymbol] = Set.empty + val nestedScopeNodes: List[ScopeNode] = s.allChildNodes + val nestedScopes: Set[ScopedInfo] = nestedScopeNodes.map(_.obj.toInfo).toSet - s.obj.toInfo - // search for defns nested within a top-level module, which are unnecessary to lift - def inModuleDefns(d: Defn): Set[BlockMemberSymbol] = - val nested = ctx.nestedDefns(d.sym) - nested.map(_.sym).toSet ++ nested.flatMap: nested => - if modules.contains(nested.sym) then inModuleDefns(nested) else Set.empty + // hack: ClassLikeSymbol does not extend DefinitionSymbol directly, so we must + // use a map to convert - val isMod = d match - case c: ClsLikeDefn => c.companion.isDefined // TODO: refine handling of companions - case _ => false + val moduleObjs = nestedScopeNodes.collect: + case ScopeNode(obj = o: ScopedObject.Companion) => o + + // TODO: refine handling of companions + for m <- moduleObjs do + ignored += m.par.isym + ignored += m.comp.isym + raise(WarningReport( + msg"Modules are not yet lifted." -> m.comp.isym.toLoc :: Nil, + N, Diagnostic.Source.Compilation + )) - val inModTopLevel = if isMod then inModuleDefns(d) else Set.empty - ignored ++= inModTopLevel + val modules: Set[ModuleOrObjSym] = moduleObjs.map(_.comp.isym).toSet + var extendsGraph: Set[(ClsSym, ClsSym)] = Set.empty // search for unliftable classes and build the extends graph - val clsSyms = clsSymToBms.values.toSet new BlockTraverser: - applyDefn(d) + this.applyScopedObject(s.obj) override def applyCase(cse: Case): Unit = cse match - case Case.Cls(cls, path) => - clsSymToBms.get(cls) match - case Some(value) if !ignored.contains(value) => // don't generate a warning if it's already ignored + case Case.Cls(cls: (ClassSymbol | ModuleOrObjectSymbol), _) => + if nestedScopes.contains(cls) && !ignored.contains(cls) then // don't generate a warning if it's already ignored raise(WarningReport( - msg"Cannot yet lift class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, + msg"Cannot yet lift class/module `${cls.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation )) - ignored += value - unliftable += value - case _ => () + ignored += cls case _ => () override def applyResult(r: Result): Unit = r match @@ -485,18 +334,14 @@ class Lifter()(using State, Raise, Config): // If B extends A, then A -> B is an edge parentPath match case None => () - case Some(path) if isIgnoredPath(path) => () - case Some(Select(RefOfBms(s, _), Tree.Ident("class"))) => - if clsSyms.contains(s) then extendsGraph += (s -> defn.sym) - case Some(RefOfBms(s, _)) => - if clsSyms.contains(s) then extendsGraph += (s -> defn.sym) - case _ if !ignored.contains(defn.sym) => + case Some(RefOfBms(_, S(s: ClassSymbol))) => + if nestedScopes.contains(s) then extendsGraph += (s -> isym) + case _ if !ignored.contains(isym) => raise(WarningReport( msg"Cannot yet lift definition `${sym.nme}` as it extends an expression." -> N :: Nil, N, Diagnostic.Source.Compilation )) - ignored += defn.sym - unliftable += defn.sym + ignored += isym case _ => () paramsOpt.foreach(applyParamList) auxParams.foreach(applyParamList) @@ -513,25 +358,24 @@ class Lifter()(using State, Raise, Config): case _ => false override def applyValue(v: Value): Unit = v match - case RefOfBms(l, _) if clsSyms.contains(l) && !modOrObj(ctx.defns(l)) => + case RefOfBms(_, S(l: ClassSymbol)) if nestedScopes.contains(l) => raise(WarningReport( msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += l - unliftable += l - case RefOfBms(l, _) if ctx.defns.contains(l) && isFun(ctx.defns(l)) => + case RefOfBms(_, S(t: TermSymbol)) => // naked reference to a function definition - firstClsFns += l + firstClsFns += t case _ => super.applyValue(v) // analyze the extends graph val extendsEdges = extendsGraph.groupBy(_._1).map: case (a, bs) => a -> bs.map(_._2) .toMap - var newUnliftable: Set[BlockMemberSymbol] = Set.empty + var newUnliftable: Set[ClsSym] = Set.empty // dfs starting from unliftable classes - def dfs(s: BlockMemberSymbol): Unit = + def dfs(s: ClsSym): Unit = for edges <- extendsEdges.get(s) b <- edges if !newUnliftable.contains(b) && !ignored.contains(b) @@ -542,10 +386,10 @@ class Lifter()(using State, Raise, Config): )) newUnliftable += b dfs(b) - for s <- ignored do + for case s: ClsLikeSym <- ignored do dfs(s) - LifterMetadata(ignored ++ newUnliftable, modules.toList, objects.toList, firstClsFns) + LifterMetadata(ignored ++ newUnliftable, modules, firstClsFns) extension (b: Block) private def floatOut(ctx: LifterCtx) = @@ -555,6 +399,8 @@ class Lifter()(using State, Raise, Config): def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + ??? + /* val AccessInfo(accessed, _, refdDefns) = ctx.getAccesses(d.sym) val inScopeRefs = refdDefns.intersect(ctx.inScopeDefns(d.sym)) @@ -599,6 +445,7 @@ class Lifter()(using State, Raise, Config): case c: ClsLikeDefn => createLiftInfoCls(c, ctx) + (d.sym -> info) case _ => Map.empty + */ def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = ctx.nestedDefns(f.sym) @@ -617,9 +464,7 @@ class Lifter()(using State, Raise, Config): // This rewrites code so that it's valid when lifted to the top level. // This way, no piece of code must be traversed by a BlockRewriter more than once. // Remark: This is why so much prior analysis is needed and is the main source of complexity in the lifter. - class BlockRewriter(inScopeIsyms: Set[InnerSymbol], ctx: LifterCtx) extends BlockTransformerShallow(SymbolSubst()): - def iSymInScope(l: InnerSymbol) = inScopeIsyms.contains(l) - + class BlockRewriter(using ctx: LifterCtxNew) extends ScopeRewriter: // Closure symbols that point to an initialized closure in this scope var activeClosures: Set[Local] = Set.empty // Map from block member symbols to initialized closures @@ -647,23 +492,11 @@ class Lifter()(using State, Raise, Config): override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version - case c @ Call(RefOfBms(l, S(d)), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModOrObj(l) => - val extraArgs = ctx.defns.get(l) match - // If it's a class, we need to add the isMut parameter. - // Instantiation without `new mut` is always immutable - case Some(c: ClsLikeDefn) => Value.Lit(Tree.BoolLit(false)).asArg :: getCallArgs(FunSyms(l, d), ctx) - case _ => getCallArgs(FunSyms(l, d), ctx) - applyListOf(args, applyArg(_)(_)): newArgs => - k(Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) - case _ => super.applyResult(r)(k) - case c @ Instantiate(mut, InstSel(l, S(d)), args) => - ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModOrObj(l) => - val extraArgs = Value.Lit(Tree.BoolLit(mut)).asArg :: getCallArgs(FunSyms(l, d), ctx) - applyListOf(args, applyArg(_)(_)): newArgs => - k(Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, config.checkInstantiateEffect, false)) - case _ => super.applyResult(r)(k) + case c @ Call(RefOfBms(l, S(d)), args) => ctx.liftedScopes.get(d) match + case None => super.applyResult(r)(k) + case Some(value) => value match + case f: LiftedFunc => k(f.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap)) + case c @ Instantiate(mut, InstSel(l, S(d)), args) => ??? // LEGACY CODE: We previously directly created the closure and assigned it to the // variable here. But, since this closure may be re-used later, this doesn't work // in general, so we will always create a TempSymbol for it. @@ -963,7 +796,7 @@ class Lifter()(using State, Raise, Config): val args2 = headPlistCopy.params.map(p => p.sym.asPath.asArg) val bdy = blockBuilder - .ret(Call(singleCallBms.asPath, args1 ++ args2)(true, true, false)) // TODO: restParams not considered + .ret(Call(singleCallBms.asPath, args1 ++ args2)(true, false, false)) // TODO: restParams not considered val mainDefn = FunDefn(f.owner, f.sym, f.dSym, PlainParamList(extraParamsCpy) :: headPlistCopy :: Nil, bdy)(false) val auxDefn = FunDefn(N, singleCallBms.b, singleCallBms.d, flatPlist, lifted.body)(forceTailRec = f.forceTailRec) @@ -1040,25 +873,29 @@ class Lifter()(using State, Raise, Config): val isMutSym = VarSymbol(Tree.Ident("isMut")) + val curSyms: MutSet[Local] = MutSet.empty + var curSym = TempSymbol(None, "tmp") + curSyms.add(curSym) def instInner(isMut: Bool) = Instantiate(mut = isMut, Value.Ref(c.sym, S(c.isym)), paramArgs) - val initSym = TempSymbol(None, "tmp") + val initSym = curSym - def go(cur: List[List[VarSymbol]], curSym: Symbol, acc: Block => Block): Block = cur match - case ps :: rst => - val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, - rst === Nil && config.checkInstantiateEffect, false) - val thisSym = TempSymbol(None, "tmp") - go(rst, thisSym, acc.assignScoped(thisSym, call)) - case Nil => acc.ret(curSym.asPath) - - val bod = go(newAuxSyms, initSym, blk => Match( + var acc: Block => Block = blk => Match( isMutSym.asPath, Case.Lit(Tree.BoolLit(true)) -> Assign(initSym, instInner(true), End()) :: Nil, S(Assign(initSym, instInner(false), End())), blk - )) + ) + + for ps <- newAuxSyms do + val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false, false) + curSym = TempSymbol(None, "tmp") + curSyms.add(curSym) + val thisSym = curSym + acc = acc.assign(thisSym, call) + // acc = blk => acc(Assign(curSym, call, blk)) + val bod = Scoped(curSyms, acc.ret(curSym.asPath)) inline def toPlist(ls: List[VarSymbol]) = PlainParamList(ls.map(s => Param(FldFlags.empty, s, N, Modulefulness.none))) @@ -1096,7 +933,7 @@ class Lifter()(using State, Raise, Config): case Some(value) => (ParamList(value.flags, extraPlist.params ++ value.params, value.restParam), auxPlist) - val auxCtorDefn_ = FunDefn(None, singleCallBms.b, singleCallBms.d, headParams :: newAuxPlist, Scoped(Set.single(initSym), bod))(false) + val auxCtorDefn_ = FunDefn(None, singleCallBms.b, singleCallBms.d, headParams :: newAuxPlist, bod)(false) val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) // Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) @@ -1225,8 +1062,9 @@ class Lifter()(using State, Raise, Config): end liftDefnsInCls - def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = - val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) + def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = ??? + /* + val (captureCls, varsMap, varsList) = createCaptureCls(???) val (blk, nested) = f.body.floatOut(ctx) @@ -1276,67 +1114,487 @@ class Lifter()(using State, Raise, Config): (if paramsSet.contains(s) then s.asPath else Value.Lit(Tree.UnitLit(true))).asArg // moved when the capture is instantiated val bod = blockBuilder - .assignScoped(captureSym, Instantiate(mut = true, // * Note: `mut` is needed for capture classes + .assign(captureSym, Instantiate(mut = true, // * Note: `mut` is needed for capture classes captureCls.sym.asPath, paramsList)) .rest(newScopedBlk) val withScope = Scoped(Set(captureSym), bod) Lifted(FunDefn(f.owner, f.sym, f.dSym, f.params, withScope)(forceTailRec = f.forceTailRec), captureCls :: newDefns) end liftDefnsInFn + */ + + given ignoredScopes: IgnoredScopes = IgnoredScopes(N) + val data = ScopeData(blk) + val metadata = data.root.children.foldLeft(LifterMetadata.empty)(_ ++ createMetadata(_)) + + def asDSym(s: ClsSym | ModuleOrObjSym): DefinitionSymbol[?] = s + val ignored: Set[ScopedInfo] = metadata.unliftable.map(asDSym) + ignoredScopes.ignored = S(ignored) + + val usedVars = UsedVarAnalyzer(blk, data, handlerPaths) + + // for debugging + def printMap[T, V](m: Map[T, V]) = + println("Map(") + for case (k, v) <- m do + print(" ") + print(k) + print(" -> ") + println(v) + println(")") + + /* + println("accessesShallow") + printMap(usedVars.shallowAccesses) + println("accesses") + printMap(usedVars.accessMap) + printMap(usedVars.accessMapWithIgnored) + println("usedVars") + printMap(usedVars.reqdCaptures) + */ + + def isIgnored(d: Defn) = d match + case f: FunDefn => ignored.contains(f.dSym) + case v: ValDefn => true + case c: ClsLikeDefn => ignored.contains(c.isym) + + case class LifterResult[+T](liftedDefn: T, extraDefns: List[Defn]) + case class LifterCtxNew( + liftedScopes: MutMap[LiftedSym, LiftedScope[?]] = MutMap.empty, + rewrittenScopes: MutMap[ScopedInfo, RewrittenScope[?]] = MutMap.empty, + var symbolsMap: Map[Local, Path] = Map.empty, + var capturesMap: Map[ScopedInfo, Path] = Map.empty + ) + + /** + * Creates a capture class for a function consisting of its mutable (and possibly immutable) local variables. + * @param f The function to create the capture class for. + * @param ctx The lifter context. Determines which variables will be captured. + * @return The tuple (defn, varsMap), where `defn` is the capture class's definition, and + * `varsMap` maps the function's locals to the corresponding `VarSymbol` (for the class parameters) in the correct order. + */ + def createCaptureCls(s: ScopedObject) + : (ClsLikeDefn, List[(Local, TermSymbol)]) = + val nme = s.nme + "$capture" - // top-level - def transform(_blk: Block) = - // this is already done once in the lowering, but the handler lowering adds lambdas currently - // so we need to desugar them again - val blk = LambdaRewriter.desugar(_blk) + val clsSym = ClassSymbol( + Tree.DummyTypeDef(syntax.Cls), + Tree.Ident(nme) + ) - val analyzer = UsedVarAnalyzer(blk) - val ctx = LifterCtx - .withLocals(analyzer.findUsedLocals) - .withDefns(analyzer.defnsMap) - .withNestedDefns(analyzer.nestedDefns) - .withAccesses(analyzer.accessMap) - .withInScopes(analyzer.inScopeDefns) - .withCompanionMap(analyzer.companionMap) + val (_, cap) = usedVars.reqdCaptures(s.toInfo) - val walker1 = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = - b match - case Define(d, rest) => - val LifterMetadata(unliftable, modules, objects, firstClsFns) = createMetadata(d, ctx) + val fresh = FreshInt() + + val sortedVars = cap.toArray.sortBy(_.uid).map: sym => + val id = fresh.make + val nme = sym.nme + "$capture$" + id + + val ident = new Tree.Ident(nme) + val varSym = VarSymbol(ident) + val fldSym = BlockMemberSymbol(nme, Nil) + val tSym = TermSymbol(syntax.MutVal, S(clsSym), ident) + + val p = Param(FldFlags.empty.copy(isVal = true), varSym, N, Modulefulness.none) + varSym.decl = S(p) // * Currently this is only accessed to create the class' toString method + + val vd = ValDefn( + tSym, + fldSym, + Value.Ref(varSym) + ) + + (sym -> varSym, p, vd) + + val defn = ClsLikeDefn( + None, clsSym, BlockMemberSymbol(nme, Nil), + S(TermSymbol(syntax.Fun, S(clsSym), clsSym.id)), + syntax.Cls, + N, + PlainParamList(sortedVars.iterator.map(_._2).toList) :: Nil, None, Nil, Nil, + Nil, + End(), + sortedVars.iterator.foldLeft[Block](End()): + case (acc, (_, _, vd)) => Define(vd, acc), + N, + N, + ) + + (defn, sortedVars.iterator.map(x => x._1._1 -> x._3.tsym).toList) + + class ScopeRewriter(using ctx: LifterCtxNew) extends BlockTransformerShallow(SymbolSubst()): + + val extraDefns: ListBuffer[Defn] = ListBuffer.empty + + def applyRewrittenScope[T](r: RewrittenScope[T]): T = + val LifterResult(rewritten, defns) = liftNestedScopes(r) + extraDefns ++= defns + rewritten + + override def applyBlock(b: Block): Block = b match + case s: Scoped => + val uid = data.getUID(s) + applyRewrittenScope(ctx.rewrittenScopes(uid)) match + case b: Block => b + case _ => die + case l: Label if l.loop => + val node = data.getNode(l.label) + val blk = applyRewrittenScope(ctx.rewrittenScopes(l.label)) match + case b: Block => b + case _ => die + l.copy(body = blk) + case _ => super.applyBlock(b) + override def applyFunDefn(fun: FunDefn) = + applyRewrittenScope(ctx.rewrittenScopes(fun.dSym)) match + case f: FunDefn => f + case _ => die + override def applyDefn(defn: Defn)(k: Defn => Block) = defn match + case f: FunDefn => k(applyFunDefn(f)) + case c: ClsLikeDefn => + val newCls = applyRewrittenScope(ctx.rewrittenScopes(c.isym)) match + case c: ClsLikeDefn => c + case _ => die + val newComp = c.companion.map: comp => + applyRewrittenScope(ctx.rewrittenScopes(comp.isym)) match + case c: ClsLikeBody => c + case _ => die + k(newCls.copy(companion = newComp)) + case _ => super.applyDefn(defn)(k) - val modObjLocals = (modules ++ objects).map: c => - analyzer.nestedIn.get(c.sym) match - case Some(bms) => - val nestedIn = analyzer.defnsMap(bms) - nestedIn match - // These will be the names of the objects/modules after being lifted - // We should use the nested object/module's **original name** if nested inside a class, - // so they can be accesed directly by name from the outside. - // For example, if a class C has an object M, (new C).M as a dynamic selection works - case cls: ClsLikeDefn => S(c.sym -> TermSymbol(syntax.ImmutVal, S(cls.isym), Tree.Ident(c.sym.nme))) - case _ => S(c.sym -> VarSymbol(Tree.Ident(c.sym.nme + "$"))) - case _ => N - .collect: - case S(v) => v - .toMap + /** + * Represents a scoped object that will be rewritten to reference the lifted version of objects and variables. + */ + sealed abstract class RewrittenScope[T](val obj: TScopedObject[T]): + val node = obj.node.get + + protected val (_, thisCapturedLocals) = usedVars.reqdCaptures(obj.toInfo) + + // These are lazy, because we don't necessarily need a captrue + private lazy val captureInfo: (ClsLikeDefn, List[(Local, TermSymbol)]) = createCaptureCls(obj) + + lazy val captureClass = captureInfo._1 + lazy val captureMap = captureInfo._2.toMap + + lazy val capturePath: Path + + /** + * Rewrites the contents of this scoped object to reference the lifted versions of variables. + * + * @return The rewritten scoped object, plus any extra scoped definitions arising from lifting the nested scoped objects. + */ + def rewrite: LifterResult[T] + + /** The path to access locals defined by this object. The primary purpose of this is to rewrite accesses + * to locals that have been moved to a capture. + */ + protected def pathsFromThisObj = + // Locals introduced by this object + val fromThisObj = data.getNode(obj.toInfo).localsWithoutLifted + .map: s => + s -> s.asPath + // Locals introduced by this object that are inside this object's capture + val fromCap = thisCapturedLocals + .map: s => + val vSym = captureMap(s) + s -> Select(capturePath, Tree.Ident(vSym.nme))(S(vSym)) + .toMap + // Note: the order here is important, as fromCap must override keys from + // fromThisObj. + fromThisObj ++ fromCap + + lazy val capturePaths = + if thisCapturedLocals.isEmpty then Map.empty + else Map(obj.toInfo -> capturePath) + + lazy val symbolsMap = pathsFromThisObj.toMap + + /** Represents a scoped object that is to be rewritten and lifted. */ + sealed abstract class LiftedScope[T <: Defn](override val obj: ScopedObject.Liftable[T])(using ctx: LifterCtxNew) extends RewrittenScope[T](obj): + private val AccessInfo(accessed, _, refdScopes) = usedVars.accessMap(obj.toInfo) + private val refdDSyms = refdScopes.collect: + case d: LiftedSym => d + .toSet + + /** Symbols that this object will lose access to once lifted, and therefore must receive + * as a parameter. Includes neighbouring objects that this definition may lose access to + * once lifted. + */ + val reqSymbols = accessed ++ node.reqCaptureObjs.map(_.sym).toSet.intersect(refdDSyms) + + private val (reqPassedSymbols, captures) = reqSymbols + .partitionMap: s => + usedVars.capturesMap.get(s) match + case Some(info) => R((s, info)) + case None => L(s) + + /** Locals that are directly passed to this object, i.e. not via a capture. */ + val passedSyms: Set[Local] = reqPassedSymbols + /** Maps locals to the scope where they were defined. */ + val capturesOrigin: Map[Local, ScopedInfo] = captures.toMap + /** Locals that are inside captures. */ + val inCaptureSyms: Set[Local] = captures.map(_._1) + /** Scopes whose captures this object requires. */ + val reqCaptures: Set[ScopedInfo] = captures.map(_._2) + + /** Maps directly passed locals to the path representing that local within this object. */ + protected val passedSymsMap: Map[Local, Path] + /** Maps scopes to the path to the path representing their captures within this object. */ + protected val capSymsMap: Map[ScopedInfo, Path] + + protected lazy val capturesOrder: List[ScopedInfo] + protected lazy val passedSymsOrder: List[Local] + + override lazy val capturePaths = + if thisCapturedLocals.isEmpty then capSymsMap + else capSymsMap + (obj.toInfo -> capturePath) + + // Note: we have to make this lazy because Scala's type system is unsound and + // lets you access the above two fields before they are initialized + // (since this constructor runs before the child classes' constructors) + + /** Maps symbols to the path representing that local within this object. + * Includes locals defined by this object's parents, and this object's own defined locals. + */ + override lazy val symbolsMap: Map[Local, Path] = + val fromParents = reqSymbols + .map: s => + passedSymsMap.get(s) match + case Some(value) => s -> value + case None => + val fromScope = capturesOrigin(s) + val capSym = capSymsMap(fromScope) + val tSym = ctx.rewrittenScopes(fromScope).captureMap(s) + s -> Select(capSym, tSym.id)(S(tSym)) + .toMap + fromParents ++ pathsFromThisObj + + def formatArgs(captures: Map[ScopedInfo, Path], locals: Map[Local, Path]): List[Arg] = + val captureArgs = capturesOrder.map(c => captures(c).asArg) + val localArgs = passedSymsOrder.map(l => locals(l).asArg) + captureArgs ::: localArgs + + /** + * A rewritten scope with a generic VarSymbol capture symbol. + */ + sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: + lazy val captureSym = VarSymbol(Tree.Ident(this.obj.nme + "$capture")) + override lazy val capturePath = captureSym.asPath + + // some helpers + private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) + private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) + private def dupParamList(plist: ParamList): ParamList = + plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) + + class RewrittenScopedBlock(override val obj: ScopedObject.ScopedBlock)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: + override def rewrite: LifterResult[Block] = + val rewriter = new ScopeRewriter: + override def applyRewrittenScope[T](r: RewrittenScope[T]): T = + val LifterResult(rewritten, defns) = liftNestedScopes(r) + extraDefns ++= defns + rewritten + + val rmved = removeLiftedScopes(obj) + val (syms, rewritten) = rmved match + case s: Scoped => (s.syms.toSet, rewriter.applyBlock(s.body)) + case b => (Set.empty, rewriter.applyBlock(b)) + LifterResult(Scoped(syms, rewritten), rewriter.extraDefns.toList) + + class RewrittenFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends RewrittenScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: + override def rewrite: LifterResult[FunDefn] = + val rewriter = new ScopeRewriter: + override def applyRewrittenScope[T](r: RewrittenScope[T]): T = + val LifterResult(rewritten, defns) = liftNestedScopes(r) + extraDefns ++= defns + rewritten + + val rewritten = rewriter.applyBlock(obj.fun.body) + LifterResult(obj.fun.copy(body = rewritten)(obj.fun.forceTailRec), rewriter.extraDefns.toList) + + class LiftedFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends LiftedScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: + private val passedSymsMap_ : Map[Local, VarSymbol] = passedSyms.map: s => + s -> VarSymbol(Tree.Ident(s.nme)) + .toMap + private val capSymsMap_ : Map[ScopedInfo, VarSymbol] = reqCaptures.map: i => + val nme = data.getNode(i).obj.nme + i -> VarSymbol(Tree.Ident(nme + "$capture")) + .toMap + + override lazy val capturesOrder: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) + override lazy val passedSymsOrder: List[Local] = passedSyms.toList.sortBy(_.uid) + + override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asPath).toMap + override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap + + val auxParams: List[Param] = + (capSymsMap_.values.toList.sortBy(_.uid) ::: passedSymsMap_.values.toList.sortBy(_.uid)) + .map(Param.simple(_)) + + val fun = obj.fun + + // Definition with the auxiliary parameters merged into the first parameter list. + private def mkFlattenedDefn: FunDefn = + val newPlists = fun.params match + case head :: next => head.copy(params = auxParams ::: head.params) :: next + case Nil => PlainParamList(auxParams) :: Nil + fun.copy(params = newPlists)(fun.forceTailRec) + + // Definition with the auxiliary parameters merged into the second parameter list. + private def mkAuxDefn: FunDefn = + val newPList = PlainParamList(dupParams(auxParams)) + val (newPlists, syms, restSym) = fun.params match + case head :: _ => + val duped = dupParamList(head) + ( + newPList :: duped :: Nil, + newPList.params.map(_.sym) ::: duped.params.map(_.sym), + duped.restParam.map(_.sym)) + // we need to append an empty param list so calling this function returns a lambda + case Nil => + ( + newPList :: PlainParamList(Nil) :: Nil, + newPList.params.map(_.sym), + N + ) + val args = restSym match + case Some(value) => + val tail = Arg(S(true), value.asPath) :: Nil + syms.foldLeft(tail): + case (acc, sym) => Arg(N, sym.asPath) :: acc + case None => syms.map(s => Arg(N, s.asPath)) + + val call = Call(Value.Ref(fun.sym, S(fun.dSym)), args)(true, true, false) + val bod = Return(call, false) + + FunDefn.withFreshSymbol( + fun.owner, + BlockMemberSymbol(fun.sym.nme + "$", Nil, fun.sym.nameIsMeaningful), + newPlists, + bod + )(false) + + val flattenedDefn = mkFlattenedDefn + val auxDefn = mkAuxDefn + + def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, Path]): Call = + Call( + Value.Ref(flattenedDefn.sym, S(flattenedDefn.dSym)), + formatArgs(captures, locals) ::: c.args + )( + isMlsFun = true, + mayRaiseEffects = c.mayRaiseEffects, + explicitTailCall = c.explicitTailCall + ) + + def rewrite: LifterResult[FunDefn] = LifterResult(obj.fun, List.empty) // stub - val ctxx = ctx - .addIgnored(unliftable) - .withModObjLocals(modObjLocals) - .withFirstClsFns(firstClsFns) - - val Lifted(lifted, extra) = d match - case f: FunDefn => - val ctxxx = ctxx.withDefnsCur(analyzer.nestedDeep(d.sym)) - liftDefnsInFn(f, ctxxx.addBmsReqdInfo(createLiftInfoFn(f, ctxxx))) - case c: ClsLikeDefn => - val ctxxx = ctxx.withDefnsCur(analyzer.nestedDeep(d.sym)) - liftDefnsInCls(c, ctxxx.addBmsReqdInfo(createLiftInfoCls(c, ctxxx))) - case _ => return super.applyBlock(b) - val newDefns = lifted :: extra - val newBms = extra.map(_.sym) - val newBlk = newDefns.foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) - Scoped(newBms.toSet, newBlk) - case _ => super.applyBlock(b) - walker1.applyBlock(blk) + /** + * Removes nested scopes that are to be lifted. + * + * @param s The scoped object whose nested scopes are to be removed. + * @return The scoped object's contents with its nested scopes removed. + */ + def removeLiftedScopes[T](s: TScopedObject[T]): T = s match + case ScopedObject.Top(b) => lastWords("Tried to remove nested scopes from the top level scope.") + case ScopedObject.Class(cls) => + val (preCtorNew, defns1) = cls.preCtor.extractDefns(isIgnored) + val (ctorNew, defns2) = cls.ctor.extractDefns(isIgnored) + cls.copy(ctor = ctorNew, preCtor = preCtorNew) + case ScopedObject.Companion(comp, par) => + val (ctorNew, defns) = comp.ctor.extractDefns(isIgnored) + comp.copy(ctor = ctorNew) + case ScopedObject.ClassCtor(cls) => () + case ScopedObject.Func(fun, isMethod) => + val (bodyNew, defns) = fun.body.extractDefns(isIgnored) + fun.copy(body = bodyNew)(fun.forceTailRec) + case ScopedObject.Loop(sym, block) => + val (blkNew, defns) = block.extractDefns(isIgnored) + blkNew + case ScopedObject.ScopedBlock(uid, block) => + val (blkNew, defns) = block.body.extractDefns(isIgnored) + val bms = defns.map(_.sym) + block.copy(block.syms.toSet -- bms, blkNew) + + private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match + case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") + case o: ScopedObject.Class => ??? + case o: ScopedObject.Companion => ??? + case o: ScopedObject.ClassCtor => ??? + case o: ScopedObject.Func => + if s.isLifted && !s.isTopLevel then LiftedFunc(o) + else RewrittenFunc(o) + case o: ScopedObject.Loop => ??? + case o: ScopedObject.ScopedBlock => + RewrittenScopedBlock(o) + + // Note: we must write this as a definition here to have tighter types + private def rewriteScope[T <: Defn](l: LiftedScope[T])(using ctx: LifterCtxNew) = + val LifterResult[T](d1, d2) = liftNestedScopes[T](l) + (d1, d2) + + /** + * Lifts scopes nested within `s`, and then rewrites `s`. + * + * @param s The scope to be rewritten. + * @param r The rewritten scope associated with `s`. + * @param ctx The lifter context. + * @return The rewritten scope with the additional definitions. + */ + private def liftNestedScopesImpl[T](scope: RewrittenScope[T])(using ctx: LifterCtxNew): LifterResult[T] = + val node = scope.node + + // Add the symbols map of the current scope + // Note: this will be reset to the original value in liftNestedScopes + ctx.symbolsMap ++= scope.symbolsMap + ctx.capturesMap ++ scope.capturePaths + + + val rewrittenScopes = node.children.map(createRewritten) + // The scopes in `lifted` will be rewritten right now + // The scopes in `ignored` will be rewritten in-place when traversing the block + val (lifted, ignored) = rewrittenScopes.partitionMap: + case s: LiftedScope[?] => L(s) + case s => R(s) + for r <- rewrittenScopes do + ctx.rewrittenScopes.put(r.obj.toInfo, r) + for l <- lifted do + ctx.liftedScopes.put(l.obj.sym, l) + + val LifterResult(rewrittenObj, extraDefns) = scope.rewrite + val (res1, res2) = lifted.map(rewriteScope).unzip + val defns = res1 ++ res2.flatten ++ extraDefns + LifterResult(rewrittenObj, defns) + + + def liftNestedScopes[T](r: RewrittenScope[T])(using ctx: LifterCtxNew): LifterResult[T] = + val curSyms = ctx.symbolsMap + val curCaptures = ctx.capturesMap + val ret = liftNestedScopesImpl(r) + ctx.symbolsMap = curSyms + ctx.capturesMap = curCaptures + ret + + def transform = + given ctx: LifterCtxNew = new LifterCtxNew + val root = data.root + var extraDefns: ListBuffer[Defn] = ListBuffer.empty + + val children = root.children + children.foreach: c => + ctx.rewrittenScopes.put(c.obj.toInfo, createRewritten(c)) + + val topLevelRewriter = new ScopeRewriter + + val (syms, top) = root.obj.contents match + case Scoped(syms, body) => + (syms.toSet, body) + case b => (Set.empty, b) + + val transformed = topLevelRewriter.applyBlock(top) + val newSyms = syms ++ extraDefns.map(_.sym) + println(extraDefns.map(_.sym)) + val withDefns = extraDefns.foldLeft(transformed): + case (acc, d) => Define(d, acc) + Scoped(newSyms, withDefns) + + \ No newline at end of file diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 800e696a27..66d3fb4512 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -1046,15 +1046,14 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val withHandlers1 = config.effectHandlers.fold(desug): opt => HandlerLowering(handlerPaths, opt).translateHandleBlocks(desug) - // TODO: Refactor the lifter so it does not require flattened scopes - val shouldFlattenScopes = config.effectHandlers.isDefined || config.liftDefns.isDefined + val shouldFlattenScopes = config.effectHandlers.isDefined val scopeFlattened = if shouldFlattenScopes then ScopeFlattener().applyBlock(withHandlers1) else withHandlers1 val lifted = - if lift then Lifter().transform(scopeFlattened) + if lift then Lifter(scopeFlattened).transform else scopeFlattened val (withHandlers2, stackSafetyInfo) = config.effectHandlers.fold((lifted, Map.empty)): opt => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala new file mode 100644 index 0000000000..590e2b6e75 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -0,0 +1,314 @@ +package hkmc2 + +import mlscript.utils.*, shorthands.* +import utils.* + +import hkmc2.codegen.* +import hkmc2.semantics.* +import hkmc2.ScopeData.* +import hkmc2.semantics.Elaborator.State + +import hkmc2.syntax.Tree +import hkmc2.codegen.llir.FreshInt +import java.util.IdentityHashMap +import scala.collection.mutable.Map as MutMap +import scala.collection.mutable.Set as MutSet + +object ScopeData: + opaque type ScopeUID = BigInt + val dummyUID: ScopeUID = 0 + class FreshUID: + private val underlying = FreshInt() + def make: ScopeUID = underlying.make + + type ScopedInfo = DefinitionSymbol[?] | LabelSymbol | ScopeUID | Unit + + // ScopeData requires the set of ignored scopes to compute certain things, but + // the lifter requires the scope tree to generate the metadata. To solve this, + // we generate the scope tree then populate the metadata later. + case class IgnoredScopes(var ignored: Opt[Set[ScopedInfo]]) + + type ScopedObject = ScopedObject.ScopedObject[?] + type TScopedObject[T] = ScopedObject.ScopedObject[T] + + type LiftedSym = DefinitionSymbol[?] + + extension (d: DefinitionSymbol[?]) + def asBmsRef = Value.Ref(d.asBlkMember.get, S(d)) + + // These cannot be hashed + object ScopedObject: + // T: The actual contents of the scoped object + sealed abstract class ScopedObject[T]: + var node: Opt[TScopeNode[T]] = N + lazy val toInfo: ScopedInfo = this match + case Top(_) => () + case Class(cls) => cls.isym + case Companion(comp, par) => comp.isym + case ClassCtor(cls) => cls.ctorSym.get + case Func(fun, _) => fun.dSym + case ScopedBlock(uid, block) => uid + case Loop(sym, _) => sym + + // note: not unique + lazy val nme = this match + case Top(b) => "top" + case Class(cls) => cls.isym.nme + case Companion(comp, par) => comp.isym.nme + "_mod" + case ClassCtor(cls) => cls.isym.nme // should be unused + case Func(fun, isMethod) => fun.dSym.nme + case Loop(sym, block) => "loop$" + sym.uid.toString() + case ScopedBlock(uid, block) => "scope$" + uid + + // Locals defined by a scoped object. + lazy val definedLocals: Set[Local] = this match + case Top(b) => b match + case Scoped(syms, _) => syms.toSet + case _ => Set.empty + case Class(cls) => + // Public fields are not included, as they are accessed using + // a field selection rather than directly using the BlockMemberSymbol. + val paramsSet: Set[Local] = cls.paramsOpt match + case Some(value) => value.params.map(_.sym).toSet + case None => Set.empty + val auxSet: Set[Local] = cls.auxParams.flatMap: p => + p.params.map(_.sym) + .toSet + paramsSet ++ auxSet ++ cls.privateFields + cls.isym + case Companion(comp, par) => + comp.privateFields.toSet + comp.isym + case _: ClassCtor => Set.empty + case Func(fun, _) => fun.params.flatMap: p => + p.params.map(_.sym) + .toSet + case ScopedBlock(_, block) => block.syms.toSet + case _: Loop => Set.empty + + def contents: T = this match + case Top(b) => b + case Class(cls) => cls + case Companion(comp, par) => comp + case ClassCtor(cls) => () + case Func(fun, _) => fun + case ScopedBlock(_, block) => block + case Loop(_, blk) => blk + + // Scoped nodes which may be referenced using a symbol. + sealed abstract class Referencable[T] extends TScopedObject[T]: + def sym: LiftedSym = this match + case Class(cls) => cls.isym + case Companion(comp, par) => comp.isym + case Func(fun, isMethod) => fun.dSym + + // Scoped nodes which could possibly be lifted to the top level. + sealed abstract class Liftable[T <: Defn] extends Referencable[T]: + val defn: T + + // The top-level scope. + case class Top(b: Block) extends ScopedObject[Block] // b may be a scoped block, in which case, its variables represent the top-level variables. + case class Class(cls: ClsLikeDefn) extends Liftable[ClsLikeDefn]: + val defn = cls + case class Companion(comp: ClsLikeBody, par: ClsLikeDefn) extends Referencable[ClsLikeBody] + // We model it like this: the ctor is just another function in the same scope as the class and initializes the corresponding class + case class ClassCtor(cls: ClsLikeDefn) extends ScopedObject[Unit] + case class Func(fun: FunDefn, isMethod: Bool) extends Liftable[FunDefn]: + val defn = fun + // The purpose of `Loop` is to enforce the rule that the control flow remains linear when we enter + // a scoped block. + case class Loop(sym: LabelSymbol, body: Block) extends ScopedObject[Block] + case class ScopedBlock(uid: ScopeUID, block: Scoped) extends ScopedObject[Block] + + extension (traverser: BlockTraverser) + def applyScopedObject(obj: ScopedObject) = + extension (s: Symbol) def traverse = + traverser.applySymbol(s) + obj match + case ScopedObject.Top(b) => traverser.applyBlock(b) + case ScopedObject.Class(ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, + privateFields, publicFields, preCtor, ctor, mod, bufferable)) + => + // do not traverse the companion -- it is a separate kind of scoped object + // and will therefore be traversed separately + own.foreach(_.traverse) + isym.traverse + sym.traverse + ctorSym.foreach(_.traverse) + paramsOpt.foreach(traverser.applyParamList) + auxParams.foreach(traverser.applyParamList) + parentPath.foreach(traverser.applyPath) + methods.foreach(traverser.applyFunDefn) + privateFields.foreach(_.traverse) + publicFields.foreach: f => + f._1.traverse; f._2.traverse + traverser.applySubBlock(preCtor) + traverser.applySubBlock(ctor) + case ScopedObject.Companion(comp, par) => traverser.applyClsLikeBody(comp) + case ScopedObject.Func(fun, isMethod) => traverser.applyFunDefn(fun) + case ScopedObject.ScopedBlock(uid, block) => traverser.applyBlock(block) + case ScopedObject.ClassCtor(c) => () + case ScopedObject.Loop(_, b) => traverser.applyBlock(b) + + // A simple tree data structure representing the nesting relation of definitions and scopes. + class NestedScopeTree(val root: TScopeNode[Block]): + val nodesMap: Map[ScopedInfo, ScopeNode] = root.allChildNodes.map(n => n.obj.toInfo -> n).toMap + + type ScopeNode = ScopeNode.ScopeNode[?] + type TScopeNode[T] = ScopeNode.ScopeNode[T] + object ScopeNode: + case class ScopeNode[T](obj: TScopedObject[T], var parent: Opt[ScopeNode[?]], children: List[ScopeNode[?]])(using ignoredScopes: IgnoredScopes): + + lazy val allParents: List[ScopeNode[?]] = parent match + case Some(value) => this :: value.allParents + case None => this :: Nil + + lazy val parentsSet = allParents.map(_.obj.toInfo).toSet + + def inSubtree(root: ScopedInfo) = parentsSet.contains(root) + + // note: includes itself + lazy val allChildNodes: List[ScopeNode[?]] = this :: children.flatMap(_.allChildNodes) + lazy val allChildren: List[ScopedObject] = allChildNodes.map(_.obj) + + // does not include variables introduced by itself + lazy val existingVars: Set[Local] = parent match + case Some(value) => value.existingVars ++ value.obj.definedLocals + case None => Set.empty + + // The following must not be called until ignoredScopes is populated with the relevant data. + + lazy val isLifted: Bool = + val ignored = ignoredScopes.ignored match + case Some(value) => value + case None => lastWords("isLifted accessed before the set of ignored scopes was set") + + parent.map(_.obj) match + case Some(_: ScopedObject.Companion) => false // there is no need to lift objects nested inside a module + case _ => + obj match + case _: ScopedObject.ScopedBlock => false + // case _: ScopedObject.Companion => false + // case c: ScopedObject.Class if c.cls.companion.isDefined => false + case ScopedObject.Func(isMethod = true) => false + case _ if ignored.contains(obj.toInfo) => false + case _ => true + + lazy val isTopLevel: Bool = parent match + case Some(ScopeNode(obj = _: ScopedObject.Top)) => true + case _ => false + + lazy val liftedChildNodes: List[ScopeNode[?]] = + if isLifted then this :: Nil + else children.flatMap(_.liftedChildNodes) + + // Finds the first parent that is a lifted object, i.e. a non-ignored definition, or the top level + lazy val firstLiftedParent: ScopedObject = + if !isLifted then + parent match + case Some(value) => value.firstLiftedParent + case None => obj // unreachable + else obj + + // When a node is lifted, some neighbouring ignored definitions may become out of scope. This computes + // the list of these definitions, and they could be passed to this node as a parameter once lifted. + private lazy val reqCaptureObjsImpl: List[ScopedObject.Referencable[?]] = obj match + case _: ScopedObject.Top => List.empty + case _ => + // All unlifted neighbour nodes ::: parent's reqCaptureObjsImpl + val initial = parent.get.allChildNodes.collect: + case c @ ScopeNode(obj = t: ScopedObject.Referencable[?]) if !c.isLifted => t + initial ::: parent.get.reqCaptureObjsImpl + + lazy val reqCaptureObjs: List[ScopedObject.Referencable[?]] = obj match + case _: ScopedObject.Top => List.empty + case _ => + if isLifted then reqCaptureObjsImpl + else parent.get.reqCaptureObjsImpl + + // Scoped blocks include the BlockMemberSymbols of their nested definitions. This removes the ones + // belonging to objects that are lifted. + lazy val localsWithoutLifted: Set[Local] = obj match + case s: ScopedObject.ScopedBlock => + val rmv = children.collect: + case c @ ScopeNode(obj = s: ScopedObject.Liftable[?]) if c.isLifted => s.defn.sym + obj.definedLocals -- rmv + case _ => obj.definedLocals + + def dSymUnapply(data: ScopeData, v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = v match + case Some(d) if data.contains(d) => S(d) + case d: DefinitionSymbol[?] if data.contains(d) => S(d) + case _ => None + +class ScopeData(b: Block)(using State, IgnoredScopes): + import ScopeData.* + + private val fresh = FreshUID() + + val scopeTree = NestedScopeTree(makeScopeTreeRec(ScopedObject.Top(b))) + val root = scopeTree.root + + def contains(s: ScopedInfo) = scopeTree.nodesMap.contains(s) + + private val scopedMap: IdentityHashMap[Scoped, ScopeUID] = new IdentityHashMap + for + case ScopeNode(obj = ScopedObject.ScopedBlock(uid, blk)) <- scopeTree.root.allChildNodes + do + scopedMap.put(blk, uid) + def getNode(x: ScopedInfo): ScopeNode = scopeTree.nodesMap(x) + def getNode(defn: ClsLikeDefn): ScopeNode = getNode(defn.isym) + def getNode(companion: ClsLikeBody): ScopeNode = getNode(companion.isym) + def getNode(defn: FunDefn): ScopeNode = getNode(defn.dSym) + def getUID(blk: Scoped): ScopeUID = + if scopedMap.containsKey(blk) then scopedMap.get(blk) + else lastWords("getUID: key not found") + def getNode(blk: Scoped): ScopeNode = getNode(getUID(blk)) + // From the input block or definition, traverses until a function, class or new scoped block is found and appends them. + class ScopeFinder extends BlockTraverserShallow: + var objs: List[ScopedObject] = Nil + override def applyBlock(b: Block): Unit = b match + case s: Scoped => + objs ::= ScopedObject.ScopedBlock(fresh.make, s) + case l: Label if l.loop => + objs ::= ScopedObject.Loop(l.label, l.body) + case _ => super.applyBlock(b) + override def applyFunDefn(fun: FunDefn): Unit = + objs ::= ScopedObject.Func(fun, false) + override def applyDefn(defn: Defn): Unit = defn match + case f: FunDefn => applyFunDefn(f) + case c: ClsLikeDefn => + objs ::= ScopedObject.Class(c) + c.ctorSym match + case Some(value) => objs ::= ScopedObject.ClassCtor(c) + case None => () + c.companion.map: comp => + objs ::= ScopedObject.Companion(comp, c) + + case _ => super.applyDefn(defn) + + + def scopeFinder = new ScopeFinder() + + def makeScopeTreeRec[T](obj: TScopedObject[T]): TScopeNode[T] = + val finder = scopeFinder + obj match + case ScopedObject.Top(s: Scoped) => finder.applyBlock(s.body) + case ScopedObject.Top(b) => finder.applyBlock(b) + case ScopedObject.Class(cls) => + finder.applyBlock(cls.preCtor) + finder.applyBlock(cls.ctor) + case ScopedObject.Companion(comp, par) => + finder.applyBlock(comp.ctor) + case ScopedObject.Func(fun, _) => + finder.applyBlock(fun.body) + case ScopedObject.ScopedBlock(_, block) => + finder.applyBlock(block.body) + case ScopedObject.ClassCtor(c) => () + case ScopedObject.Loop(_, b) => finder.applyBlock(b) + val mtdObjs = obj match + case ScopedObject.Class(cls) => cls.methods.map(ScopedObject.Func(_, true)) + case ScopedObject.Companion(comp, par) => comp.methods.map(ScopedObject.Func(_, true)) + case _ => Nil + val children = (mtdObjs ::: finder.objs).map(makeScopeTreeRec) + val retNode = ScopeNode.ScopeNode(obj, N, children) + obj.node = S(retNode) + for c <- children do c.parent = S(retNode) + retNode diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 4b5c976354..27db68d747 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -9,9 +9,29 @@ import hkmc2.codegen.* import hkmc2.semantics.* import hkmc2.Message.* import hkmc2.semantics.Elaborator.State +import hkmc2.ScopeData.* +import hkmc2.Lifter.* import scala.collection.mutable.Map as MutMap - +import scala.collection.mutable.Set as MutSet +import scala.jdk.CollectionConverters.* +import java.util.IdentityHashMap +import java.util.Collections +import scala.collection.mutable.Buffer + +object UsedVarAnalyzer: + case class MutAccessInfo( + accessed: MutSet[Local], + mutated: MutSet[Local], + refdDefns: MutSet[ScopedInfo] + ): + def toIMut = AccessInfo(accessed.toSet, mutated.toSet, refdDefns.toSet) + object MutAccessInfo: + def empty = MutAccessInfo( + MutSet.empty, + MutSet.empty, + MutSet.empty + ) /** * Analyzes which variables have been used and mutated by which functions. * Also finds which variables can be passed to a capture class without a heap @@ -19,256 +39,264 @@ import scala.collection.mutable.Map as MutMap * * Assumes the input trees have no lambdas. */ -class UsedVarAnalyzer(b: Block)(using State): - import Lifter.* - - private case class DefnMetadata( - definedLocals: Map[BlockMemberSymbol, Set[Local]], // locals defined explicitly by that function - defnsMap: Map[BlockMemberSymbol, Defn], // map bms to defn - existingVars: Map[BlockMemberSymbol, Set[Local]], // variables already existing when that defn is defined - inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]], // definitions that are in scope and not nested within this defn, and not including itself - nestedDefns: Map[BlockMemberSymbol, List[Defn]], // definitions that are a successor of the current defn - nestedDeep: Map[BlockMemberSymbol, Set[BlockMemberSymbol]], // definitions nested within another defn, including that defn (deep) - nestedIn: Map[BlockMemberSymbol, BlockMemberSymbol], // the definition that a definition is directly nested in - companionMap: Map[InnerSymbol, InnerSymbol], // a (bijective) map between companion object symbols and class symbols - ) - private def createMetadata: DefnMetadata = - var defnsMap: Map[BlockMemberSymbol, Defn] = Map.empty - var definedLocals: Map[BlockMemberSymbol, Set[Local]] = Map.empty - var existingVars: Map[BlockMemberSymbol, Set[Local]] = Map.empty - var inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty - var nestedDefns: Map[BlockMemberSymbol, List[Defn]] = Map.empty - var nestedDeep: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty - var nestedIn: Map[BlockMemberSymbol, BlockMemberSymbol] = Map.empty - var companionMap: Map[InnerSymbol, InnerSymbol] = Map.empty - - def createMetadataFn(f: FunDefn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = - var nested: Set[BlockMemberSymbol] = Set.empty - - existingVars += (f.sym -> existing) - val thisVars = Lifter.getVars(f) -- existing - val newExisting = existing ++ thisVars - - val thisScopeDefns: List[Defn] = f.body.gatherDefns() - - nestedDefns += f.sym -> thisScopeDefns - - val newInScope = inScope ++ thisScopeDefns.map(_.sym) - for s <- thisScopeDefns do - inScopeDefns += s.sym -> (newInScope - s.sym) - nested += s.sym - - defnsMap += (f.sym -> f) - definedLocals += (f.sym -> thisVars) - - for d <- thisScopeDefns do - nestedIn += (d.sym -> f.sym) - createMetadataDefn(d, newExisting, newInScope) - nested ++= nestedDeep(d.sym) - - nestedDeep += f.sym -> nested - - def createMetadataDefn(d: Defn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = - d match - case f: FunDefn => - createMetadataFn(f, existing, inScope) - case c: ClsLikeDefn => - createMetadataCls(c, existing, inScope) - case d => Map.empty - - def createMetadataCls(c: ClsLikeDefn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = - var nested: Set[BlockMemberSymbol] = Set.empty - - existingVars += (c.sym -> existing) - val thisVars = Lifter.getVars(c) -- existing - val newExisting = existing ++ thisVars - - val thisScopeDefns: List[Defn] = c.methods ++ c.preCtor.gatherDefns() - ++ c.ctor.gatherDefns() ++ c.companion.fold(Nil)(comp => comp.ctor.gatherDefns() ++ comp.methods) - - nestedDefns += c.sym -> thisScopeDefns - - val newInScope = inScope ++ thisScopeDefns.map(_.sym) - for s <- thisScopeDefns do - inScopeDefns += s.sym -> (newInScope - s.sym) - nested += s.sym - - defnsMap += (c.sym -> c) - definedLocals += (c.sym -> thisVars) - - for d <- thisScopeDefns do - nestedIn += (d.sym -> c.sym) - createMetadataDefn(d, newExisting, newInScope) - nested ++= nestedDeep(d.sym) - - nestedDeep += c.sym -> nested - - c.companion match - case None => - case Some(value) => companionMap += (value.isym -> c.isym) - +class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes): + import UsedVarAnalyzer.* + + object SDSym: + def unapply(v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = dSymUnapply(scopeData, v) + + // Finds the locals that this block accesses/mutates, and the definitions which it could use. + private def blkAccessesShallow(b: Block): AccessInfo = + var accessed: MutAccessInfo = MutAccessInfo.empty new BlockTraverserShallow: - // If there's any variables available at the top-level we need to explicitly ignore, - // then we add them here - val ignoredVars = b.definedVars applyBlock(b) - override def applyDefn(defn: Defn): Unit = - inScopeDefns += defn.sym -> Set.empty - createMetadataDefn(defn, ignoredVars, Set.empty) - DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn, companionMap) - - val DefnMetadata(definedLocals, defnsMap, existingVars, - inScopeDefns, nestedDefns, nestedDeep, nestedIn, companionMap) = createMetadata - - private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty - private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = - cacheId.flatMap(blkMutCache.get) match - case Some(value) => value - case None => - var accessed: AccessInfo = AccessInfo.empty - new BlockTraverserShallow: - applyBlock(b) - - override def applyBlock(b: Block): Unit = b match - case Assign(lhs, rhs, rest) => - accessed = accessed.addMutated(lhs) - applyResult(rhs) - applyBlock(rest) - case Label(label, loop, body, rest) => - accessed ++= blkAccessesShallow(body, S(label)) - applyBlock(rest) - case _ => super.applyBlock(b) - - override def applyValue(v: Value): Unit = v match - case Value.Ref(_: BuiltinSymbol, _) => super.applyValue(v) - case RefOfBms(l, _) => - accessed = accessed.addRefdDefn(l) - case Value.Ref(l, _) => - accessed = accessed.addAccess(l) - case _ => super.applyValue(v) - - cacheId match - case None => () - case Some(value) => blkMutCache.addOne(value -> accessed) - accessed - - private val accessedCache: MutMap[BlockMemberSymbol, AccessInfo] = MutMap.empty - + override def applyBlock(b: Block): Unit = b match + case s: Scoped => + accessed.refdDefns.add(scopeData.getUID(s)) + case Assign(lhs, rhs, rest) => + accessed.mutated.add(lhs) + applyResult(rhs) + applyBlock(rest) + case l: Label if l.loop => + accessed.refdDefns.add(l.label) + case d: Define => applySubBlock(d.rest) + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Unit = p match + case Value.Ref(_: BuiltinSymbol, _) => super.applyPath(p) + case RefOfBms(_, SDSym(dSym)) if scopeData.contains(dSym) => + accessed.refdDefns.add(scopeData.getNode(dSym).obj.toInfo) + case Value.Ref(l, _) => + accessed.accessed.add(l) + case _ => super.applyPath(p) + accessed.toIMut + /** - * Finds the variables which this definition could possibly mutate, excluding mutations through - * calls to other functions and, in the case of functions, mutations of its own variables. - * - * @param defn The definition to search through. + * Finds the variables belonging to a parent scope which this scoped object could possibly + * access or mutate, excluding mutations through calls to other functions and mutations + * of their own variables. Also finds the other scoped objects that this definition may enter. + * + * @param obj The scoped object to search through. * @return The variables which this definition could possibly mutate. */ - private def findAccessesShallow(defn: Defn): AccessInfo = - def create = defn match - case f: FunDefn => - val fVars = definedLocals(f.sym) - blkAccessesShallow(f.body).withoutLocals(fVars) - case c: ClsLikeDefn => - val methodSyms = c.methods.map(_.sym).toSet - c.methods.foldLeft(blkAccessesShallow(c.preCtor) ++ blkAccessesShallow(c.ctor)): - case (acc, fDefn) => - // class methods do not need to be lifted, so we don't count calls to their methods. - // a previous reference to this class's block member symbol is enough to assume any - // of the class's methods could be called. - // - // however, we must keep references to the class itself! - val defnAccess = findAccessesShallow(fDefn) - acc ++ defnAccess.withoutBms(methodSyms) - case _: ValDefn => AccessInfo.empty - - accessedCache.getOrElseUpdate(defn.sym, create) - - // MUST be called from a top-level defn - private def findAccesses(d: Defn): Map[BlockMemberSymbol, AccessInfo] = - var defns: mutable.Buffer[Defn] = mutable.Buffer.empty - var definedVarsDeep: Set[Local] = Set.empty + private def findAccessesShallow(obj: ScopedObject): AccessInfo = + val accessed = obj match + case ScopedObject.Top(b) => b match + case s: Scoped => blkAccessesShallow(s.body) + case _ => blkAccessesShallow(b) + case ScopedObject.Func(f, _) => + blkAccessesShallow(f.body) + case ScopedObject.Class(c) => + // We must assume that classes may access all their methods. + // When the class symbol is referenced once, that symbol may be used in + // arbitrary ways, which includes calling any of this class's methods. + val res = blkAccessesShallow(c.preCtor) ++ blkAccessesShallow(c.ctor) + res.copy(refdDefns = res.refdDefns ++ c.methods.map(_.dSym)) + case ScopedObject.ClassCtor(cls) => + // Recall that we interpret the ctor as just another function in the same scope + // as the corresponding class, and initializes the class. + AccessInfo.empty.addRefdScopedObj(scopeData.getNode(cls).obj.toInfo) + case ScopedObject.ScopedBlock(uid, b) => blkAccessesShallow(b.body) + case ScopedObject.Companion(c, _) => + // There likely won't be nested companion classes in the future, but for now, + // just assume they may access all their methods + val res = blkAccessesShallow(c.ctor) + res.copy(refdDefns = res.refdDefns ++ c.methods.map(_.dSym)) + case ScopedObject.Loop(_, b) => blkAccessesShallow(b) + // Variables introduced by this scoped object do not belong to a parent scope, so + // we remove them + accessed.withoutLocals(obj.definedLocals) + + private def combineInfos(m1: Map[ScopedInfo, AccessInfo], m2: Map[ScopedInfo, AccessInfo]): Map[ScopedInfo, AccessInfo] = + if m2.size < m1.size then combineInfos(m2, m1) + else m1.foldLeft(m2): + case (acc, info -> accesses) => m2.get(info) match + case Some(value) => acc + (info -> (accesses ++ value)) + case None => acc + (info -> accesses) + + val shallowAccesses: Map[ScopedInfo, AccessInfo] = + scopeData.scopeTree.root.allChildren.map(obj => obj.toInfo -> findAccessesShallow(obj)).toMap + + // Optimization: Find all nodes which are accessed by their children + // See the comment for findAccesses + private val allEdges = + for + (src, accesses) <- shallowAccesses + refd <- accesses.refdDefns + if src =/= refd + yield + (src, refd) + private val accessedByChild = allEdges + .groupBy(_._2) // group by edge destination + .map: + case (_: Unit) -> _ => () -> false + case d -> edges => + val par = scopeData.getNode(d).parent.get.obj.toInfo + d -> edges.exists: + case a -> b => a =/= par + .collect: + case d -> true => d + .toSet - new BlockTraverser: - applyDefn(d) - - override def applyFunDefn(f: FunDefn): Unit = - defns += f - definedVarsDeep ++= definedLocals(f.sym) - super.applyFunDefn(f) - - override def applyDefn(defn: Defn): Unit = - defn match - case c: ClsLikeDefn => - defns += c - definedVarsDeep ++= definedLocals(c.sym) - case _ => - super.applyDefn(defn) + // Find: + // - Map 1: + // - Variables that each scoped object has accessed, either through itself or a nested scoped object. + // - Variables that each scoped object has mutated, either through itself or a nested scoped object. + // - Scoped objects that each object accesses, either through itself or a nested scoped object. + // - Map 2: + // - Variables that each scoped object has accessed, either through itself or a *lifted* scoped object. + // - Variables that each scoped object has mutated, either through itself or a lifted nested scoped object. + // - Scoped objects that each object accesses, either through itself or a lifted nested scoped object. + // + // The former includes ignored objects, and is used to do the readers/writers analysis. The latter is used to determine + // whether we actually need to allocate a capture for the object. In particular, we never need to allocate a capture + // for a variable if only nested scopes mutate it. + // + // Note that it is possible for a lifted scoped object to be reached by traversing through an ignored object. + // + // Also observe that if a node is not accesed from any of its children, then we can re-use the result of its parent's analysis. + private def findAccesses(s: ScopeNode): (Map[ScopedInfo, AccessInfo], Map[ScopedInfo, AccessInfo]) = + // Note: these include `s` + val children = s.allChildren + val childInfo = children.map(_.toInfo).toSet + + // Traverses the node's children, and stops when a child that is accessed by one of its children is found. + // The analysis will be performed on *all* of the traversed nodes simultaneously. + // We will later recurse on the children of all these nodes. + val nexts: Buffer[ScopeNode] = Buffer.empty + def findNodes(s: ScopeNode): List[ScopeNode] = s :: s.children.flatMap: child => + if accessedByChild(child.obj.toInfo) then + nexts.addOne(child) + List.empty + else findNodes(child) + val nodes = findNodes(s) + + val allLocals = nodes.flatMap(node => node.obj.definedLocals).toSet - val defnSyms = defns.iterator.map(_.sym).toSet - val accessInfo = defns.map: d => - val AccessInfo(accessed, mutated, refdDefns) = findAccessesShallow(d) - d.sym -> AccessInfo( - accessed.intersect(definedVarsDeep), - mutated.intersect(definedVarsDeep), - refdDefns.intersect(defnSyms) // only care about definitions nested in this top-level definition + val accessInfo = children.map: obj => + val a @ AccessInfo(accessed, mutated, refdDefns) = shallowAccesses(obj.toInfo) + obj.toInfo -> AccessInfo( + accessed = accessed.intersect(allLocals), + mutated = mutated.intersect(allLocals), + refdDefns = refdDefns.intersect(childInfo) ) val accessInfoMap = accessInfo.toMap - - val edges = + val edges: Set[(ScopedInfo, ScopedInfo)] = for - (sym, AccessInfo(_, _, refd)) <- accessInfo + (src, AccessInfo(_, _, refd)) <- accessInfo r <- refd - if defnSyms.contains(r) - yield sym -> r + // remove self-edges: they do not affect this analysis + if src =/= r + // very important: we only care about edges that flow into the subtree rooted at `s` + if childInfo.contains(r) && r =/= s.obj.toInfo + yield src -> r .toSet // (sccs, sccEdges) forms a directed acyclic graph (DAG) - val algorithms.SccsInfo(sccs, sccEdges, inDegs, outDegs) = algorithms.sccsWithInfo(edges, defnSyms) - - // all defns in the same scc must have at least the same accesses as each other - val base = for (id, scc) <- sccs yield id -> - scc.foldLeft(AccessInfo.empty): - case (acc, sym) => acc ++ accessInfoMap(sym) + val algorithms.SccsInfo(sccs, sccEdges, inDegs, outDegs) = algorithms.sccsWithInfo(edges, childInfo) + + val rootInfo = s.obj.toInfo + val (rootId, rootElems) = sccs.find: + case (id, elems) => elems.contains(rootInfo) + .get + if rootElems.size != 1 then lastWords("SCC containing root had a degree other than 1.") - // dp on DAG - val dp: MutMap[Int, AccessInfo] = MutMap.empty - def sccAccessInfo(scc: Int): AccessInfo = dp.get(scc) match - case Some(value) => value - case None => - val ret = sccEdges(scc).foldLeft(base(scc)): - case (acc, nextScc) => acc ++ sccAccessInfo(nextScc) - dp.addOne(scc -> ret) - ret + // With respect to the current scoped object `s`, we may "ignore" one of its children `c` if and only if + // it is ignored (not lifted), and `s` is in the subtree rooted at the first lifted parent of `c`. We + // "ignore" `c` in the sense that it does not need to capture `s`'s scoped object's variables, nor does + // it require the current scoped object to create a capture class for its accessed variables. + def isIgnored(c: ScopedInfo) = + s.inSubtree(scopeData.getNode(c).firstLiftedParent.toInfo) + + // All objects in the same scc must have at least the same accesses as each other + def go(includeIgnored: Bool) = + val base = for (id, scc) <- sccs yield + // If all objects in this SCC are ignored, then we treat it as if it does not access anything, + // unless we explicitly want to count ignored items (for the readers-mutators analysis) + if !includeIgnored && scc.forall(isIgnored) then id -> AccessInfo.empty + else id -> scc.foldLeft(AccessInfo.empty): + case (acc, sym) => acc ++ accessInfoMap(sym) + + // dp on DAG + val dp: MutMap[Int, AccessInfo] = MutMap.empty + def sccAccessInfo(scc: Int): AccessInfo = dp.get(scc) match + case Some(value) => value + case None => + val ret = sccEdges(scc).foldLeft(base(scc)): + case (acc, nextScc) => acc ++ sccAccessInfo(nextScc) + dp.addOne(scc -> ret) + ret + + for + (id, scc) <- sccs + sym <- scc + yield + sym -> sccAccessInfo(id).withoutLocals(scopeData.getNode(sym).obj.definedLocals) - for - (id, scc) <- sccs - sym <- scc - yield sym -> (sccAccessInfo(id).intersectLocals(existingVars(sym))) + val (m1, m2) = (go(true), go(false)) + val subCases = nexts.map(findAccesses) + subCases.foldLeft((m1, m2)): + case ((acc1, acc2), (new1, new2)) => (combineInfos(acc1, new1), combineInfos(acc2, new2)) - private def findAccessesTop = - var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty - new BlockTraverserShallow: - applyBlock(b) - override def applyDefn(defn: Defn): Unit = defn match - case _: FunDefn | _: ClsLikeDefn => - accessMap ++= findAccesses(defn) - case _ => super.applyDefn(defn) + // Searching from the root makes no sense. We instead start searching from each scope nested in the top-level + private val (m1, m2) = scopeData.scopeTree.root.children.map(findAccesses).unzip + val accessMapWithIgnored = m1.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) + val accessMap = m2.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) + + private def reqdCaptureLocals(s: ScopeNode): Map[ScopedInfo, (Set[Local], Set[Local])] = + val (blk, extraMtds) = s.obj match + case ScopedObject.Top(b) => lastWords("reqdCaptureLocals called on top block") + case ScopedObject.ClassCtor(cls) => return Map.empty + (s.obj.toInfo -> (Set.empty, Set.empty)) + case ScopedObject.Class(cls) => (Begin(cls.preCtor, cls.ctor), cls.methods) + case ScopedObject.Companion(comp, _) => (comp.ctor, comp.methods) + case ScopedObject.Func(fun, _) => (fun.body, Nil) + case ScopedObject.ScopedBlock(uid, block) => (block, Nil) + case ScopedObject.Loop(sym, block) => (block, Nil) - accessMap - - val accessMap = findAccessesTop - - // TODO: let declarations inside loops (also broken without class lifting) - // I'll fix it once it's fixed in the IR since we will have more tools to determine - // what locals belong to what block. - private def reqdCaptureLocals(f: FunDefn) = - var defns = f.body.gatherDefns() - val defnSyms = defns.collect: - case f: FunDefn => f.sym -> f - case c: ClsLikeDefn => c.sym -> c + // traverse all scoped blocks + val nexts: Buffer[ScopeNode] = Buffer.empty + def findNodes(s: ScopeNode): List[ScopeNode] = s :: s.children.flatMap: + case c @ ScopeNode(obj = obj: ScopedObject.ScopedBlock) => findNodes(c) + case c => + nexts.addOne(c) + List.empty + val nodes = findNodes(s) + + val locals = nodes.flatMap(_.obj.definedLocals).toSet + + val (read, cap) = reqdCaptureLocalsBlk(blk, nexts.toList, locals) + + // Variables mutated by a lifted child of a class methods requires a capture + val additional = extraMtds + .flatMap: mtd => + scopeData.getNode(mtd).liftedChildNodes.map(x => x.obj.toInfo) + .foldLeft(AccessInfo.empty): + case (acc, value) => acc ++ accessMap(value) + val (newRead, newCap) = (read ++ additional.accessed, cap ++ additional.mutated) + + val (usedVarsL, mutatedVarsL) = nexts.map: node => + val a = accessMap(node.obj.toInfo) + (a.accessed, a.mutated) + .unzip + + val usedVars = usedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) + val mutatedVars = mutatedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) + + val cur: Map[ScopedInfo, (Set[Local], Set[Local])] = nodes.map: n => + n.obj.toInfo -> ( + newRead.intersect(usedVars).intersect(n.obj.definedLocals), + newCap.intersect(mutatedVars).intersect(n.obj.definedLocals) + ) .toMap + + nexts.foldLeft(cur): + case (mp, acc) => mp ++ reqdCaptureLocals(acc) - val thisVars = definedLocals(f.sym) + // readers-mutators analysis + private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], thisVars: Set[Local]): (Set[Local], Set[Local]) = + val scopeInfos: Map[ScopedInfo, ScopeNode] = nextNodes.map(node => node.obj.toInfo -> node).toMap case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local]) @@ -288,6 +316,10 @@ class UsedVarAnalyzer(b: Block)(using State): new BlockTraverserShallow: applyBlock(b) override def applyBlock(b: Block): Unit = b match + // Note that we traverse directly into scoped blocks without using handleCalledScope + + case l: Label if l.loop => + handleCalledScope(l.label) case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs @@ -300,18 +332,8 @@ class UsedVarAnalyzer(b: Block)(using State): val dfltInfo = dflt.map: case arm => rec(arm) - infos.map(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive - dfltInfo.map(merge) - applyBlock(rest) - case Label(label, loop, body, rest) => - // for now, if the loop body mutates a variable and that variable is accessed or mutated by a defn, - // or if it reads a variable that is later mutated by an instance inside the loop, - // we put it in a capture. this preserves the current semantics of the IR (even though it's incorrect). - // See the above TODO - val c @ CaptureInfo(req, read, mut) = rec(body) - merge(c) - reqCapture ++= read.intersect(blkAccessesShallow(body, S(label)).mutated) - reqCapture ++= mut.intersect(body.freeVars) + infos.foreach(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive + dfltInfo.foreach(merge) applyBlock(rest) case Begin(sub, rest) => rec(sub) |> merge @@ -327,16 +349,22 @@ class UsedVarAnalyzer(b: Block)(using State): hasMutator = Set.empty case _ => super.applyBlock(b) - def handleCalledBms(called: BlockMemberSymbol): Unit = defnSyms.get(called) match + def handleCalledScope(called: ScopedInfo): Unit = scopeInfos.get(called) match case None => () - case Some(defn) => - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + case Some(node) => + val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(called) val muts = muted.intersect(thisVars) - val reads = defn.freeVars.intersect(thisVars) -- muts + val reads = accessed.intersect(thisVars) -- muts + val refdExcl = refd.filter: sym => + scopeData.getNode(sym).obj match + case s: ScopedObject.ScopedBlock => false + case ScopedObject.Func(_, true) => false + case _ => true + // this not a naked reference. if it's a ref to a class, this can only ever create once instance // so the "one writer" rule applies for l <- muts do - if hasReader.contains(l) || hasMutator.contains(l) || defn.isInstanceOf[FunDefn] then + if hasReader.contains(l) || hasMutator.contains(l) then reqCapture += l hasMutator += l for l <- reads do @@ -347,34 +375,33 @@ class UsedVarAnalyzer(b: Block)(using State): // function, we must capture the latter's mutated variables in a capture, as arbitrarily // many mutators could be created from it for - sym <- refd - l <- accessMap(sym).mutated + sym <- refdExcl + l <- accessMapWithIgnored(sym).mutated do reqCapture += l hasMutator += l - override def applyResult(r: Result): Unit = r match - case Call(RefOfBms(l, _), args) => - args.map(super.applyArg(_)) - handleCalledBms(l) - case Instantiate(mut, InstSel(l), args) => - args.map(super.applyArg) - handleCalledBms(l._1) + override def applyResult(r: Result): Unit = + r match + case Call(RefOfBms(_, SDSym(d)), args) => + args.foreach(super.applyArg(_)) + handleCalledScope(d) + case Instantiate(mut, InstSel(_, S(d)), args) => + args.foreach(super.applyArg) + handleCalledScope(d) case _ => super.applyResult(r) override def applyPath(p: Path): Unit = p match - case RefOfBms(l, _) => - defnSyms.get(l) match + case RefOfBms(_, SDSym(d)) => + scopeInfos.get(d) match case None => super.applyPath(p) case Some(defn) => - val isMod = defn match - case c: ClsLikeDefn => modOrObj(c) - case _ => false + val isMod = defn.obj.isInstanceOf[ScopedObject.Companion] if isMod then super.applyPath(p) else - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(d) val muts = muted.intersect(thisVars) - val reads = defn.freeVars.intersect(thisVars) -- muts + val reads = accessed.intersect(thisVars) -- muts // this is a naked reference, we assume things it mutates always needs a capture for l <- muts do reqCapture += l @@ -388,7 +415,7 @@ class UsedVarAnalyzer(b: Block)(using State): // many mutators could be created from it for sym <- refd - l <- accessMap(sym).mutated + l <- accessMapWithIgnored(sym).mutated do reqCapture += l hasMutator += l @@ -399,52 +426,29 @@ class UsedVarAnalyzer(b: Block)(using State): override def applyDefn(defn: Defn): Unit = defn match case c: ClsLikeDefn if modOrObj(c) => - handleCalledBms(c.sym) + handleCalledScope(c.isym) super.applyDefn(defn) case _ => super.applyDefn(defn) CaptureInfo(reqCapture, hasReader, hasMutator) - val reqCapture = go(f.body, Set.empty, Set.empty, Set.empty).reqCapture - val usedVars = defns.flatMap(_.freeVars.intersect(thisVars)).toSet - (usedVars, reqCapture) - - // the current problem is that we need extra code to find which variables were really defined by a function - // this may be resolved in the future when the IR gets explicit variable declarations - private def findUsedLocalsFn(f: FunDefn): Map[BlockMemberSymbol, FreeVars] = - val thisVars = definedLocals(f.sym) - - val (vars, cap) = reqdCaptureLocals(f) - - var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - usedMap += (f.sym -> Lifter.FreeVars(vars.intersect(thisVars), cap.intersect(thisVars))) - for d <- nestedDefns(f.sym) do - usedMap ++= findUsedLocalsDefn(d) - usedMap + val (usedVarsL, mutatedVarsL) = scopeInfos.map: + case (info, node) => + val a = accessMap(info).intersectLocals(thisVars) + (a.accessed, a.mutated) + .unzip + val usedVars = usedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) + val mutatedVars = mutatedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) + val reqCapture = go(b, Set.empty, Set.empty, Set.empty).reqCapture.intersect(mutatedVars) - private def findUsedLocalsDefn(d: Defn) = - d match - case f: FunDefn => - findUsedLocalsFn(f) - case c: ClsLikeDefn => - findUsedLocalsCls(c) - case d => Map.empty + (usedVars, reqCapture) - private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = - nestedDefns(c.sym).foldLeft(Map.empty): - case (acc, d) => acc ++ findUsedLocalsDefn(d) + val reqdCaptures: Map[ScopedInfo, (Set[Local], Set[Local])] = scopeData.root.children.foldLeft(Map.empty): + case (acc, node) => acc ++ reqdCaptureLocals(node) - /** - * Finds the used locals of functions which have been used by their nested definitions. - * - * @param b - * @return - */ - def findUsedLocals: Lifter.UsedLocalsMap = - var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - new BlockTraverserShallow: - applyBlock(b) - override def applyDefn(defn: Defn): Unit = - usedMap ++= findUsedLocalsDefn(defn) - - Lifter.UsedLocalsMap(usedMap) + // For local inside a capture, finds the node to which this local belongs. + val capturesMap = + for + case (info -> (_, reqCap)) <- reqdCaptures + s <- reqCap + yield s -> info From dcc42cc95fc32dab5798c5dd87c2026fee623603 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 15 Jan 2026 22:21:56 +0800 Subject: [PATCH 02/18] function lifting working --- .../shared/src/main/scala/hkmc2/Config.scala | 4 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 580 +++++------------- .../main/scala/hkmc2/codegen/ScopeData.scala | 5 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 39 +- .../src/test/mlscript/lifter/FunInFun.mls | 447 ++++++++++---- 5 files changed, 537 insertions(+), 538 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index ce12c08f42..bbbb1d736b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -63,9 +63,9 @@ object Config: case class EffectHandlers( debug: Bool, stackSafety: Opt[StackSafety], - // Whether we check `Instantiate` nodes for effects, currently no effect can be raised in a constructor. + // Whether we check `Instantiate` nodes for effects. Currently, effects cannot be raised in constructors. checkInstantiateEffect: Bool = false, - // A debug option that allow codegen to continue even if a unlifted definition is encountered. + // A debug option that allows codegen to continue even if an unlifted definition is encountered. softLifterError: Bool = false ) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index e6aedfa4f3..abe69cd190 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -216,24 +216,29 @@ class Lifter(blk: Block)(using State, Raise): object LifterCtx: def empty = LifterCtx() def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) - + + extension (l: Local) + def asLocalPath: LocalPath = LocalPath.Sym(l) + enum LocalPath: case Sym(l: Local) + case BmsRef(l: BlockMemberSymbol, d: DefinitionSymbol[?]) + case InCapture(capturePath: Path, field: TermSymbol) case PubField(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol) def read = this match case Sym(l) => l.asPath + case BmsRef(l, d) => Value.Ref(l, S(d)) + case InCapture(path, field) => Select(path, field.id)(S(field)) case PubField(isym, sym) => Select(isym.asPath, Tree.Ident(sym.nme))(N) def asArg = read.asArg def assign(value: Result, rest: Block) = this match case Sym(l) => Assign(l, value, rest) + case BmsRef(l, d) => lastWords("Tried to assign to a BlockMemberSymbol") + case InCapture(path, field) => AssignField(path, field.id, value, rest)(S(field)) case PubField(isym, sym) => AssignField(isym.asPath, Tree.Ident(sym.nme), value, rest)(S(sym)) - - def readDisamb(d: Opt[DefinitionSymbol[?]]) = this match - case Sym(l) => Value.Ref(l, d) - case PubField(isym, sym) => Select(isym.asPath, Tree.Ident(sym.nme))(d) case class FunSyms[T <: DefinitionSymbol[?]](b: BlockMemberSymbol, d: T): def asPath = Value.Ref(b, S(d)) @@ -507,24 +512,31 @@ class Lifter(blk: Block)(using State, Raise): // extract the call override def applyPath(p: Path)(k: Path => Block): Block = p match - case RefOfBms(l, S(d)) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => - val newSym = closureMap.get(l) match - case None => - // $this was previously used, but it may be confused with the `this` keyword - // let's use $here instead - val newSym = TempSymbol(N, l.nme + "$here") - extraLocals.add(newSym) - syms.addOne(FunSyms(l, d) -> newSym) // add to `syms`: this closure will be initialized in `applyBlock` - closureMap.addOne(l -> newSym) // add to `closureMap`: `newSym` refers to the closure and can be used later - newSym + case r @ RefOfBms(l, S(d)) => ctx.liftedScopes.get(d) match + case S(f: LiftedFunc) => + if f.isTrivial then k(r) + else + val newSym = closureMap.get(l) match + case None => + val newSym = TempSymbol(N, l.nme + "$here") + extraLocals.add(newSym) + syms.addOne(FunSyms(l, d) -> newSym) // add to `syms`: this closure will be initialized in `applyBlock` + closureMap.addOne(l -> newSym) // add to `closureMap`: `newSym` refers to the closure and can be used later + newSym - // symbol exists, and is initialized - case Some(value) if activeClosures.contains(value) => value - // symbol exists, needs initialization - case Some(value) => - syms.addOne(FunSyms(l, d) -> value) - value - k(Value.Ref(newSym, S(d))) + // symbol exists, and is initialized + case Some(value) if activeClosures.contains(value) => value + // symbol exists, needs initialization + case Some(value) => + syms.addOne(FunSyms(l, d) -> value) + value + k(Value.Ref(newSym, S(d))) + + // Other naked references to BlockMemberSymbols. + case N => ctx.symbolsMap.get(d) match + case Some(value) => k(value.read) + case None => super.applyPath(p)(k) + case _ => super.applyPath(p)(k) (walker.applyBlock(b), syms.toList, extraLocals) end rewriteBms @@ -535,17 +547,15 @@ class Lifter(blk: Block)(using State, Raise): activeClosures = curActive ret - override def applyBlock(b: Block): Block = + override def applyBlock(b: Block): Block = // extract references to BlockMemberSymbols in the block which now may // need to be enriched with aux parameters val (rewritten, syms, extras) = rewriteBms(b) extraLocals.addAll(extras) val pre = syms.foldLeft(blockBuilder): - case (blk, (bms, local)) => - val initial = blk.assign(local, createCall(bms, ctx)) - ctx.defns(bms.b) match - case c: ClsLikeDefn => initial.assignFieldN(local.asPath, Tree.Ident("class"), bms.asPath) - case _ => initial + case (blk, (funSym, local)) => + ctx.liftedScopes(funSym.d) match + case l: LiftedFunc => blk.assign(local, l.rewriteRef(ctx.capturesMap, ctx.symbolsMap)) // Rewrite the rest val remaining = rewritten match @@ -571,52 +581,36 @@ class Lifter(blk: Block)(using State, Raise): (dflt2 is dflt) && (rst2 is rst) then b else Match(scrut2, arms2, dflt2, rst2) - case Label(lbl, loop, bod, rst) => + case Label(lbl, false, bod, rst) => val lbl2 = lbl.subst val bod2 = applySubBlockAndReset(bod) val rst2 = applySubBlock(rst) - if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, loop, bod2, rst2) + if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, false, bod2, rst2) case TryBlock(sub, fin, rst) => val sub2 = applySubBlockAndReset(sub) val fin2 = applySubBlockAndReset(fin) val rst2 = applySubBlock(rst) if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then b else TryBlock(sub2, fin2, rst2) - // Detect private field usages - case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => - ctx.resolveIsymPath(t.owner.get) match - case Some(value) if !iSymInScope(t.owner.get) => - if (t.k is syntax.LetBind) && !t.owner.forall(_.isInstanceOf[semantics.TopLevelSymbol]) then - // TODO: improve the error message - raise(ErrorReport( - msg"Uses of private fields cannot yet be lifted." -> N :: Nil, - N, Diagnostic.Source.Compilation - )) - applyResult(rhs): newRhs => - AssignField(value.read, t.id, newRhs, applyBlock(rest))(N) - case _ => super.applyBlock(rewritten) - // Assignment to variables - case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match - case Some(captureSym) => - applyResult(rhs): newRhs => - AssignField(ctx.getLocalClosPath(lhs).get.read, captureSym.id, newRhs, applyBlock(rest))(N) - case None => ctx.getLocalPath(lhs) match - case None => super.applyBlock(rewritten) - case Some(value) => - applyResult(rhs): newRhs => - value.assign(newRhs, applyBlock(rest)) + case Assign(lhs, rhs, rest) => ctx.symbolsMap.get(lhs) match + case Some(path) => applyResult(rhs): rhs2 => + path.assign(rhs2, applySubBlock(rest)) + case _ => super.applyBlock(b) // rewrite ValDefns (in ctors) - case Define(d: ValDefn, rest: Block) if d.owner.isDefined => + case Define(d: ValDefn, rest: Block) if d.owner.isDefined => super.applyBlock(b) // TODO + /* ctx.getIsymPath(d.owner.get) match case Some(value) if !iSymInScope(d.owner.get) => applyResult(d.rhs): newRhs => AssignField(value.read, Tree.Ident(d.sym.nme), newRhs, applyBlock(rest))(S(d.sym)) case _ => super.applyBlock(rewritten) - + */ // rewrite object definitions, assigning to the given symbol in modObjLocals - case Define(d: ClsLikeDefn, rest: Block) => ctx.modObjLocals.get(d.sym) match + case Define(d: ClsLikeDefn, rest: Block) => super.applyBlock(b) // TODO + /* + ctx.modObjLocals.get(d.sym) match case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match case Some(_) => // has args extraLocals.add(sym) @@ -631,74 +625,31 @@ class Lifter(blk: Block)(using State, Raise): case _ => ctx.replacedDefns.get(d.sym) match case Some(value) => Define(value, applyBlock(rest)) case None => super.applyBlock(rewritten) - + */ case _ => super.applyBlock(rewritten) pre.rest(remaining) override def applyPath(p: Path)(k: Path => Block): Block = p match - // These two cases rewrites `this.whatever` when referencing an outer class's fields. - case Value.Ref(l: InnerSymbol, _) => - ctx.resolveIsymPath(l) match - case Some(value) if !iSymInScope(l) => k(value.read) - case _ => super.applyPath(p)(k) - case Value.Ref(t: TermSymbol, _) if t.owner.isDefined => - ctx.resolveIsymPath(t.owner.get) match - case Some(value) if !iSymInScope(t.owner.get) => - if (t.k is syntax.LetBind) && !t.owner.forall(_.isInstanceOf[semantics.TopLevelSymbol]) then - // TODO: improve the error message - raise(ErrorReport( - msg"Uses of private fields cannot yet be lifted." -> N :: Nil, - N, Diagnostic.Source.Compilation - )) - k(Select(value.read, t.id)(N)) - case _ => super.applyPath(p)(k) - // For objects inside classes: When an object is nested inside a class, its defn will be // replaced by a symbol, to which the object instance is assigned. This rewrites references // from the objects BlockMemberSymbol to that new symbol. - case s @ Select(qual, ident) => + // case s @ Select(qual, ident) => ??? + /* s.symbol.flatMap(ctx.getLocalPath) match case Some(LocalPath.Sym(value: DefinitionSymbol[?])) => k(Select(qual, Tree.Ident(value.nme))(S(value))) case _ => super.applyPath(p)(k) - - // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol - // reference is passed as function parameters). - case RefOfBms(l, disamb) if ctx.ignored(l) && ctx.isRelevant(l) => ctx.getIgnoredBmsPath(l) match - case Some(value) => k(value.readDisamb(disamb)) - case None => super.applyPath(p)(k) + */ + + // This rewrites naked references to locals, + case Value.Ref(l, _) => ctx.symbolsMap.get(l) match + case Some(value) => k(value.read) + case _ => super.applyPath(p)(k) - // This rewrites naked references to locals. If a function is in a capture, then we select that value - // from the capture; otherwise, we see if that local is passed directly as a parameter to this defn. - case Value.Ref(l, _) => ctx.getLocalCaptureSym(l) match - case Some(captureSym) => - k(Select(ctx.getLocalClosPath(l).get.read, captureSym.id)(N)) - case None => ctx.getLocalPath(l) match - case Some(value) => k(value.read) - case None => super.applyPath(p)(k) case _ => super.applyPath(p)(k) - - // When calling a lifted function or constructor, we need to pass, as arguments, the local variables, - // inner symbols, etc that it needs to access. This function creates those arguments for that in - // the correct order. - def getCallArgs(sym: FunSyms[?], ctx: LifterCtx) = - val info = ctx.getBmsReqdInfo(sym.b).get - val localsArgs = info.reqdVars.map(s => ctx.getLocalPath(s).get.asArg) - val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) - val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asArg) - val bmsArgs = info.reqdBms.map(ctx.getIgnoredBmsPath(_).get.asArg) - bmsArgs ++ iSymArgs ++ localsArgs ++ capturesArgs - // This creates a call to a lifted function or constructor. - def createCall(syms: FunSyms[?], ctx: LifterCtx): Call = - val info = ctx.getBmsReqdInfo(syms.b).get - val callSym = info.fakeCtorBms match - case Some(v) => v - case None => syms - Call(callSym.asPath, getCallArgs(syms, ctx))(false, false, false) - /* * Explanation of liftOutDefnCont, liftDefnsInCls, liftDefnsInFn: * @@ -715,96 +666,19 @@ class Lifter(blk: Block)(using State, Raise): */ def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match case N => d match - case f: FunDefn => liftDefnsInFn(f, ctx) - case c: ClsLikeDefn => liftDefnsInCls(c, ctx) + case f: FunDefn => ??? + case c: ClsLikeDefn => ??? case _ => Lifted(d, Nil) case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms)) => def createSymbolsUpdateCtx[T <: LocalPath](createSym: String => (VarSymbol, T)) : (List[Param], LifterCtx, List[(Local, (VarSymbol, T))]) = - val capturesSymbols = includedCaptures.map: sym => - (sym, createSym(sym.nme + "$capture")) - - val localsSymbols = includedLocals.map: sym => - (sym, createSym(sym.nme)) - - val isymSymbols = clsCaptures.map: sym => - (sym, createSym(sym.nme + "$instance")) - - val bmsSymbols = reqdBms.map: sym => - (sym, createSym(sym.nme + "$member")) - - val extraParamsCaptures = capturesSymbols.map: // parameter list - case (d, (sym, _)) => Param(FldFlags.empty, sym, N, Modulefulness.none) - val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol - case (d, (_, lp)) => d -> lp - .toMap - - val extraParamsLocals = localsSymbols.map: // parameter list - case (d, (sym, _)) => Param(FldFlags.empty, sym, N, Modulefulness.none) - val newLocalsPaths = localsSymbols.map: // mapping from sym to param symbol - case (d, (_, lp)) => d -> lp - .toMap - - val extraParamsIsyms = isymSymbols.map: // parameter list - case (d, (sym, _)) => Param(FldFlags.empty, sym, N, Modulefulness.none) - val newIsymPaths = isymSymbols.map: // mapping from sym to param symbol - case (d, (_, lp)) => d -> lp - .toMap - - val extraParamsBms = bmsSymbols.map: // parameter list - case (d, (sym, _)) => Param(FldFlags.empty, sym, N, Modulefulness.none) - val newBmsPaths = bmsSymbols.map: // mapping from sym to param symbol - case (d, (_, lp)) => d -> lp - .toMap - - val extraParams = extraParamsBms ++ extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures - - val newCtx = ctx - .replCapturePaths(newCapturePaths) - .replLocalPaths(newLocalsPaths) - .addIsymPaths(newIsymPaths) - .replIgnoredBmsPaths(newBmsPaths) - - (extraParams, newCtx, capturesSymbols ++ localsSymbols ++ isymSymbols ++ bmsSymbols) + ??? d match case f: FunDefn => - def createSym(nme: String) = - val vsym = VarSymbol(Tree.Ident(nme)) - (vsym, LocalPath.Sym(vsym)) - val (extraParams, newCtx, _) = createSymbolsUpdateCtx(createSym) - - // create second param list with different symbols - val extraParamsCpy = extraParams.map(p => p.copy(sym = VarSymbol(p.sym.id))) - - val headPlistCopy = f.params.headOption match - case None => PlainParamList(Nil) - case Some(value) => ParamList(value.flags, value.params.map(p => p.copy(sym = VarSymbol(p.sym.id))), value.restParam) - - val flatPlist = f.params match - case head :: next => ParamList(head.flags, extraParams ++ head.params, head.restParam) :: next - case Nil => PlainParamList(extraParams) :: Nil - - val newDef = FunDefn( - base.owner, f.sym, f.dSym, PlainParamList(extraParams) :: f.params, f.body - )(f.forceTailRec) - val Lifted(lifted, extras) = liftDefnsInFn(newDef, newCtx) - - val args1 = extraParamsCpy.map(p => p.sym.asPath.asArg) - val args2 = headPlistCopy.params.map(p => p.sym.asPath.asArg) - - val bdy = blockBuilder - .ret(Call(singleCallBms.asPath, args1 ++ args2)(true, false, false)) // TODO: restParams not considered - - val mainDefn = FunDefn(f.owner, f.sym, f.dSym, PlainParamList(extraParamsCpy) :: headPlistCopy :: Nil, bdy)(false) - val auxDefn = FunDefn(N, singleCallBms.b, singleCallBms.d, flatPlist, lifted.body)(forceTailRec = f.forceTailRec) - - if ctx.firstClsFns.contains(f.sym) then - Lifted(mainDefn, auxDefn :: extras) - else - Lifted(auxDefn, extras) // we can include just the flattened defn + ??? case c: ClsLikeDefn => val fresh = FreshInt() def createSym(nme: String): (VarSymbol, LocalPath.PubField) = @@ -838,7 +712,7 @@ class Lifter(blk: Block)(using State, Raise): publicFields = newPubFields, ctor = newCtor ) - liftDefnsInCls(newDef, newCtx) + ??? else // normal class val newDef = c.copy( @@ -848,7 +722,7 @@ class Lifter(blk: Block)(using State, Raise): ctor = newCtor ) - val Lifted(lifted, extras) = liftDefnsInCls(newDef, newCtx) + val Lifted(lifted, extras) = ??? val bms = fakeCtorBms.get @@ -942,187 +816,6 @@ class Lifter(blk: Block)(using State, Raise): end liftOutDefnCont - def removeDefnsFromScope(b: Block, defns: List[Defn]) = b match - case Scoped(syms, body) => Scoped(syms.toSet -- defns.map(_.sym), body) - case _ => b - - def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = - val ctxx = if c.companion.isDefined then ctx.inModule(c) else ctx // TODO: refine handling of companions - - // =========================================================== - // STEP 1: lift out definitions nested in the ctor and prector - // and deal with class defns in the companion class - - val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctxx) - val (ctor, ctorDefns) = c.ctor.floatOut(ctxx) - val (cCtor, cCtorDefns) = c.companion.fold((None, Nil)): - case value => - val (a, b) = value.ctor.floatOut(ctxx) - (S(a), b) - - val allCtorDefns = preCtorDefns ++ ctorDefns ++ cCtorDefns - - // ctorIgnored: definitions within the class (i.e. ctor) that we don't lift - // ctorIncluded: ditto, but lifted - val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctxx.ignored(d.sym)) - - // Symbols containing refernces to nested classes and nested objects are here - - // Deals with references to lifted objects defined within the class - val nestedClsPaths: Map[Local, LocalPath] = ctorIncluded.map: - case c: ClsLikeDefn if modOrObj(c) => ctxx.modObjLocals.get(c.sym) match - case Some(sym) => S(c.isym -> LocalPath.Sym(sym)) - case _ => S(c.sym -> LocalPath.Sym(c.sym)) - case _ => None - .collect: - case Some(x) => x - .toMap - - val newCtx_ = ctxx - // references to lifted objects - .addLocalPaths(nestedClsPaths) - // references to variables defined in the class, including ones in the companion obj - .addLocalPaths(getVars(c).map(s => s -> LocalPath.Sym(s)).toMap) - // reference to unlifted BMS's - .addIgnoredBmsPaths(ctorIgnored.map(d => d.sym -> LocalPath.Sym(d.sym)).toMap) - .addIsymPath(c.isym, LocalPath.Sym(c.isym)) - .inISym(c.isym) - - // add the reference to `this` if it has a companion object - val newCtx = c.companion match - case None => newCtx_ - case Some(value) => newCtx_.addIsymPath(value.isym, LocalPath.Sym(c.sym)).inISym(value.isym) - - // lifts the liftable definitions - // lifted defns can no longer access currently in-scope isyms, so call resetScope - val ctorDefnsLifted = ctorIncluded.flatMap: defn => - val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx.flushModules.resetScope) - liftedDefn :: extraDefns - - // we still need to lift out definitions within unliftable defns - val ctorIgnoredLift = ctorIgnored.map: defn => - liftOutDefnCont(c, defn, newCtx) - - // we still need to rewrite definitions that aren't lifted - // this map tells us how to rewrite the Defns - val ctorIgnoredExtra = ctorIgnoredLift.flatMap(_.extraDefns) - val ctorIgnoredRewrite = ctorIgnoredLift.map: lifted => - lifted.liftedDefn.sym -> lifted.liftedDefn - .toMap - - val replacedDefnsCtx = newCtx.addreplacedDefns(ctorIgnoredRewrite) - val rewriter = BlockRewriter(newCtx.inScopeISyms, replacedDefnsCtx) - val newPreCtor = removeDefnsFromScope(rewriter.rewrite(preCtor), ctorIncluded) - val newCtor = removeDefnsFromScope(rewriter.rewrite(ctor), ctorIncluded) - val newCCtor = cCtor.map(blk => removeDefnsFromScope(rewriter.rewrite(blk), ctorIncluded)) - - // =========================================================== - // STEP 2: rewrite non-static class methods - - val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) - val methods = fLifted.collect: - case Lifted(liftedDefn, extraDefns) => liftedDefn - val fExtra = fLifted.flatMap: - case Lifted(liftedDefn, extraDefns) => extraDefns - - // =========================================================== - // STEP 3: rewrite companion class methods - - val cfLifted = c.companion.fold(Nil)(_.methods.map(liftDefnsInFn(_, newCtx))) - - val cMethods = cfLifted.collect: - case Lifted(liftedDefn, extraDefns) => liftedDefn - val cfExtra = cfLifted.flatMap: - case Lifted(liftedDefn, extraDefns) => extraDefns - - val newCompanion = c.companion.fold(None): - case value => S(value.copy(methods = cMethods, ctor = newCCtor.get)) - - val extras = (ctorDefnsLifted ++ fExtra ++ cfExtra ++ ctorIgnoredExtra).map: - case f: FunDefn => f.copy(owner = N)(forceTailRec = f.forceTailRec) - case c: ClsLikeDefn => c.copy(owner = N) - case d => d - - def rewriteExtends(p: Path): Path = p match - case RefOfBms(b, _) if !ctx.ignored(b) && ctx.isRelevant(b) => b.asPath - case _ => return p - - // if this class extends something, rewrite - val newPar = c.parentPath.map(rewriteExtends) - - val newDef = c.copy( - methods = methods, - preCtor = newPreCtor, - parentPath = newPar, - ctor = newCtor, - companion = newCompanion, - ) - - Lifted(newDef, extras) - - end liftDefnsInCls - - def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = ??? - /* - val (captureCls, varsMap, varsList) = createCaptureCls(???) - - val (blk, nested) = f.body.floatOut(ctx) - - val (ignored, included) = nested.partition(d => ctx.ignored(d.sym)) - - val modPaths: Map[Local, LocalPath] = nested.map: - case c: ClsLikeDefn if modOrObj(c) => ctx.modObjLocals.get(c.sym) match - case Some(sym) => S(c.sym -> LocalPath.Sym(sym)) - case _ => S(c.sym -> LocalPath.Sym(c.sym)) - case _ => None - .collect: - case Some(x) => x - .toMap - - val thisVars = ctx.usedLocals(f.sym) - // add the mapping from this function's locals to the capture's symbols and the capture path - val captureSym = TempSymbol(N, "capture") - val captureCtx = ctx - .addLocalCaptureSyms(varsMap) // how to access locals via the capture class from now on - .addCapturePath(f.sym, LocalPath.Sym(captureSym)) // the path to this function's capture - .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> LocalPath.Sym(s)).toMap) - .addLocalPaths(modPaths) - .addIgnoredBmsPaths(ignored.map(d => d.sym -> LocalPath.Sym(d.sym)).toMap) - val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) - - // lift out the nested defns - // for lifted definitions, any accessible isyms go out of scope, so call resetScope - val nestedLifted = included.map(liftOutDefnCont(f, _, nestedCtx.flushModules.resetScope)) - val ignoredLifted = ignored.map(liftOutDefnCont(f, _, nestedCtx)) - val ignoredExtra = ignoredLifted.flatMap(_.extraDefns) - val newDefns = ignoredExtra ++ nestedLifted.flatMap: - case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns - - val ignoredRewrite = ignoredLifted.map: lifted => - lifted.liftedDefn.sym -> lifted.liftedDefn - .toMap - - val transformed = BlockRewriter(ctx.inScopeISyms, captureCtx.addreplacedDefns(ignoredRewrite)).rewrite(blk) - val newScopedBlk = removeDefnsFromScope(transformed, included) - - if thisVars.reqCapture.size == 0 then - Lifted(FunDefn(f.owner, f.sym, f.dSym, f.params, newScopedBlk)(forceTailRec = f.forceTailRec), newDefns) - else - // move the function's parameters to the capture - val paramsSet = f.params.flatMap(_.paramSyms) - val paramsList = varsList.map: s => - (if paramsSet.contains(s) then s.asPath else Value.Lit(Tree.UnitLit(true))).asArg - // moved when the capture is instantiated - val bod = blockBuilder - .assign(captureSym, Instantiate(mut = true, // * Note: `mut` is needed for capture classes - captureCls.sym.asPath, paramsList)) - .rest(newScopedBlk) - val withScope = Scoped(Set(captureSym), bod) - Lifted(FunDefn(f.owner, f.sym, f.dSym, f.params, withScope)(forceTailRec = f.forceTailRec), captureCls :: newDefns) - - end liftDefnsInFn - */ - given ignoredScopes: IgnoredScopes = IgnoredScopes(N) val data = ScopeData(blk) val metadata = data.root.children.foldLeft(LifterMetadata.empty)(_ ++ createMetadata(_)) @@ -1131,7 +824,7 @@ class Lifter(blk: Block)(using State, Raise): val ignored: Set[ScopedInfo] = metadata.unliftable.map(asDSym) ignoredScopes.ignored = S(ignored) - val usedVars = UsedVarAnalyzer(blk, data, handlerPaths) + val usedVars = UsedVarAnalyzer(blk, data) // for debugging def printMap[T, V](m: Map[T, V]) = @@ -1162,7 +855,7 @@ class Lifter(blk: Block)(using State, Raise): case class LifterCtxNew( liftedScopes: MutMap[LiftedSym, LiftedScope[?]] = MutMap.empty, rewrittenScopes: MutMap[ScopedInfo, RewrittenScope[?]] = MutMap.empty, - var symbolsMap: Map[Local, Path] = Map.empty, + var symbolsMap: Map[Local, LocalPath] = Map.empty, var capturesMap: Map[ScopedInfo, Path] = Map.empty ) @@ -1268,6 +961,7 @@ class Lifter(blk: Block)(using State, Raise): val node = obj.node.get protected val (_, thisCapturedLocals) = usedVars.reqdCaptures(obj.toInfo) + val hasCapture = !thisCapturedLocals.isEmpty // These are lazy, because we don't necessarily need a captrue private lazy val captureInfo: (ClsLikeDefn, List[(Local, TermSymbol)]) = createCaptureCls(obj) @@ -1276,37 +970,49 @@ class Lifter(blk: Block)(using State, Raise): lazy val captureMap = captureInfo._2.toMap lazy val capturePath: Path + + protected def rewriteImpl: LifterResult[T] /** * Rewrites the contents of this scoped object to reference the lifted versions of variables. * * @return The rewritten scoped object, plus any extra scoped definitions arising from lifting the nested scoped objects. */ - def rewrite: LifterResult[T] + def rewrite = + if hasCapture then + val LifterResult(defn, extra) = rewriteImpl + LifterResult(defn, captureClass :: extra) + else rewriteImpl /** The path to access locals defined by this object. The primary purpose of this is to rewrite accesses * to locals that have been moved to a capture. */ - protected def pathsFromThisObj = + protected def pathsFromThisObj: Map[Local, LocalPath] = // Locals introduced by this object val fromThisObj = data.getNode(obj.toInfo).localsWithoutLifted .map: s => - s -> s.asPath + s -> s.asLocalPath + .toMap // Locals introduced by this object that are inside this object's capture val fromCap = thisCapturedLocals .map: s => val vSym = captureMap(s) - s -> Select(capturePath, Tree.Ident(vSym.nme))(S(vSym)) + s -> LocalPath.InCapture(capturePath, vSym) .toMap + // BMS refs from ignored defns + // Note that we map the DefinitionSymbol to the disambiguated BMS. + val fromIgnored = node.children.collect: + case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => + r.sym -> LocalPath.BmsRef(r.bsym, r.sym) // Note: the order here is important, as fromCap must override keys from // fromThisObj. - fromThisObj ++ fromCap + fromThisObj ++ fromCap ++ fromIgnored lazy val capturePaths = if thisCapturedLocals.isEmpty then Map.empty else Map(obj.toInfo -> capturePath) - lazy val symbolsMap = pathsFromThisObj.toMap + lazy val symbolsMap: Map[Local, LocalPath] = pathsFromThisObj.toMap /** Represents a scoped object that is to be rewritten and lifted. */ sealed abstract class LiftedScope[T <: Defn](override val obj: ScopedObject.Liftable[T])(using ctx: LifterCtxNew) extends RewrittenScope[T](obj): @@ -1337,7 +1043,7 @@ class Lifter(blk: Block)(using State, Raise): val reqCaptures: Set[ScopedInfo] = captures.map(_._2) /** Maps directly passed locals to the path representing that local within this object. */ - protected val passedSymsMap: Map[Local, Path] + protected val passedSymsMap: Map[Local, LocalPath] /** Maps scopes to the path to the path representing their captures within this object. */ protected val capSymsMap: Map[ScopedInfo, Path] @@ -1355,20 +1061,22 @@ class Lifter(blk: Block)(using State, Raise): /** Maps symbols to the path representing that local within this object. * Includes locals defined by this object's parents, and this object's own defined locals. */ - override lazy val symbolsMap: Map[Local, Path] = + override lazy val symbolsMap: Map[Local, LocalPath] = val fromParents = reqSymbols .map: s => passedSymsMap.get(s) match + // The symbol is passed directly case Some(value) => s -> value + // The symbol is passed in a capture case None => val fromScope = capturesOrigin(s) val capSym = capSymsMap(fromScope) val tSym = ctx.rewrittenScopes(fromScope).captureMap(s) - s -> Select(capSym, tSym.id)(S(tSym)) + s -> LocalPath.InCapture(capSym, tSym) .toMap fromParents ++ pathsFromThisObj - def formatArgs(captures: Map[ScopedInfo, Path], locals: Map[Local, Path]): List[Arg] = + def formatArgs(captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): List[Arg] = val captureArgs = capturesOrder.map(c => captures(c).asArg) val localArgs = passedSymsOrder.map(l => locals(l).asArg) captureArgs ::: localArgs @@ -1379,6 +1087,21 @@ class Lifter(blk: Block)(using State, Raise): sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: lazy val captureSym = VarSymbol(Tree.Ident(this.obj.nme + "$capture")) override lazy val capturePath = captureSym.asPath + + protected def addCaptureSym(b: Block): Block = + if hasCapture then + val undef = Value.Lit(Tree.UnitLit(false)).asArg + val inst = Instantiate( + true, + Value.Ref(captureClass.sym, S(captureClass.isym)), + List.fill(thisCapturedLocals.size)(undef) + ) + Scoped( + Set(captureSym), + Assign(captureSym, inst, b) + ) + else + b // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) @@ -1387,29 +1110,23 @@ class Lifter(blk: Block)(using State, Raise): plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) class RewrittenScopedBlock(override val obj: ScopedObject.ScopedBlock)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: - override def rewrite: LifterResult[Block] = - val rewriter = new ScopeRewriter: - override def applyRewrittenScope[T](r: RewrittenScope[T]): T = - val LifterResult(rewritten, defns) = liftNestedScopes(r) - extraDefns ++= defns - rewritten + override def rewriteImpl: LifterResult[Block] = + val rewriter = new BlockRewriter val rmved = removeLiftedScopes(obj) val (syms, rewritten) = rmved match - case s: Scoped => (s.syms.toSet, rewriter.applyBlock(s.body)) - case b => (Set.empty, rewriter.applyBlock(b)) - LifterResult(Scoped(syms, rewritten), rewriter.extraDefns.toList) + case s: Scoped => (s.syms.toSet, rewriter.rewrite(s.body)) + case b => (Set.empty, rewriter.rewrite(b)) + val withCapture = addCaptureSym(rewritten) + LifterResult(Scoped(syms, withCapture), rewriter.extraDefns.toList) class RewrittenFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends RewrittenScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: - override def rewrite: LifterResult[FunDefn] = - val rewriter = new ScopeRewriter: - override def applyRewrittenScope[T](r: RewrittenScope[T]): T = - val LifterResult(rewritten, defns) = liftNestedScopes(r) - extraDefns ++= defns - rewritten + override def rewriteImpl: LifterResult[FunDefn] = + val rewriter = new BlockRewriter - val rewritten = rewriter.applyBlock(obj.fun.body) - LifterResult(obj.fun.copy(body = rewritten)(obj.fun.forceTailRec), rewriter.extraDefns.toList) + val rewritten = rewriter.rewrite(obj.fun.body) + val withCapture = addCaptureSym(rewritten) + LifterResult(obj.fun.copy(body = withCapture)(obj.fun.forceTailRec), rewriter.extraDefns.toList) class LiftedFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends LiftedScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: private val passedSymsMap_ : Map[Local, VarSymbol] = passedSyms.map: s => @@ -1423,21 +1140,32 @@ class Lifter(blk: Block)(using State, Raise): override lazy val capturesOrder: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) override lazy val passedSymsOrder: List[Local] = passedSyms.toList.sortBy(_.uid) - override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asPath).toMap + override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap val auxParams: List[Param] = (capSymsMap_.values.toList.sortBy(_.uid) ::: passedSymsMap_.values.toList.sortBy(_.uid)) .map(Param.simple(_)) + // Whether this can be lifted without the need to pass extra parameters. + val isTrivial = auxParams.isEmpty + val fun = obj.fun + val (mainSym, mainDsym) = (fun.sym, fun.dSym) + val auxSym = BlockMemberSymbol(fun.sym.nme + "$", Nil, fun.sym.nameIsMeaningful) + val auxDsym = TermSymbol.fromFunBms(auxSym, fun.owner) + // Definition with the auxiliary parameters merged into the first parameter list. - private def mkFlattenedDefn: FunDefn = + private def mkFlattenedDefn: LifterResult[FunDefn] = val newPlists = fun.params match case head :: next => head.copy(params = auxParams ::: head.params) :: next case Nil => PlainParamList(auxParams) :: Nil - fun.copy(params = newPlists)(fun.forceTailRec) + val rewriter = new BlockRewriter + val newBod = rewriter.rewrite(fun.body) + val withCapture = addCaptureSym(newBod) + val newDefn = fun.copy(sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)(fun.forceTailRec) + LifterResult(newDefn, rewriter.extraDefns.toList) // Definition with the auxiliary parameters merged into the second parameter list. private def mkAuxDefn: FunDefn = @@ -1466,27 +1194,40 @@ class Lifter(blk: Block)(using State, Raise): val call = Call(Value.Ref(fun.sym, S(fun.dSym)), args)(true, true, false) val bod = Return(call, false) - FunDefn.withFreshSymbol( + FunDefn( fun.owner, - BlockMemberSymbol(fun.sym.nme + "$", Nil, fun.sym.nameIsMeaningful), + auxSym, + auxDsym, newPlists, bod )(false) - val flattenedDefn = mkFlattenedDefn - val auxDefn = mkAuxDefn - - def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, Path]): Call = + def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = + if isTrivial then c + else + Call( + Value.Ref(mainSym, S(mainDsym)), + formatArgs(captures, locals) ::: c.args + )( + isMlsFun = true, + mayRaiseEffects = c.mayRaiseEffects, + explicitTailCall = c.explicitTailCall + ) + + def rewriteRef(captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = Call( - Value.Ref(flattenedDefn.sym, S(flattenedDefn.dSym)), - formatArgs(captures, locals) ::: c.args + Value.Ref(auxSym, S(auxDsym)), + formatArgs(captures, locals) )( isMlsFun = true, - mayRaiseEffects = c.mayRaiseEffects, - explicitTailCall = c.explicitTailCall + mayRaiseEffects = false, + explicitTailCall = false ) - def rewrite: LifterResult[FunDefn] = LifterResult(obj.fun, List.empty) // stub + def rewriteImpl: LifterResult[FunDefn] = + val LifterResult(lifted, extra) = mkFlattenedDefn + LifterResult(lifted, mkAuxDefn :: extra) + /** * Removes nested scopes that are to be lifted. @@ -1546,8 +1287,7 @@ class Lifter(blk: Block)(using State, Raise): // Add the symbols map of the current scope // Note: this will be reset to the original value in liftNestedScopes ctx.symbolsMap ++= scope.symbolsMap - ctx.capturesMap ++ scope.capturePaths - + ctx.capturesMap ++= scope.capturePaths val rewrittenScopes = node.children.map(createRewritten) // The scopes in `lifted` will be rewritten right now @@ -1577,7 +1317,6 @@ class Lifter(blk: Block)(using State, Raise): def transform = given ctx: LifterCtxNew = new LifterCtxNew val root = data.root - var extraDefns: ListBuffer[Defn] = ListBuffer.empty val children = root.children children.foreach: c => @@ -1591,9 +1330,8 @@ class Lifter(blk: Block)(using State, Raise): case b => (Set.empty, b) val transformed = topLevelRewriter.applyBlock(top) - val newSyms = syms ++ extraDefns.map(_.sym) - println(extraDefns.map(_.sym)) - val withDefns = extraDefns.foldLeft(transformed): + val newSyms = syms ++ topLevelRewriter.extraDefns.map(_.sym) + val withDefns = topLevelRewriter.extraDefns.foldLeft(transformed): case (acc, d) => Define(d, acc) Scoped(newSyms, withDefns) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index 590e2b6e75..68994bc37e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -99,6 +99,11 @@ object ScopeData: case Class(cls) => cls.isym case Companion(comp, par) => comp.isym case Func(fun, isMethod) => fun.dSym + def bsym: BlockMemberSymbol = this match + case Class(cls) => cls.sym + case Companion(comp, par) => par.sym + case Func(fun, isMethod) => fun.sym + // Scoped nodes which could possibly be lifted to the top level. sealed abstract class Liftable[T <: Defn] extends Referencable[T]: diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index ef38e8b363..588a2e4b4d 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -9,4 +9,41 @@ // :todo - +:lift +:sjs +fun f(x, y) = + fun g(a)(z) = + set x += 1 + y + [g, g] +//│ JS (unsanitized): +//│ let g, f, f$capture1, g$; +//│ g$ = function g$(f$captureAAAA, y) { +//│ return (a) => { +//│ return g(f$captureAAAA, y, a) +//│ } +//│ }; +//│ g = function g(f$captureAAAA, y, a) { +//│ return (z) => { +//│ let tmp; +//│ tmp = f$captureAAAA.x$capture$0 + 1; +//│ f$captureAAAA.x$capture$0 = tmp; +//│ return y +//│ } +//│ }; +//│ (class f$capture { +//│ static { +//│ f$capture1 = this +//│ } +//│ constructor(x$capture$0) { +//│ this.x$capture$0 = x$capture$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "f$capture"]; +//│ }); +//│ f = function f(x, y) { +//│ let f$capture2, g$here; +//│ f$capture2 = new f$capture1(undefined); +//│ g$here = g$(f$capture2, y); +//│ return globalThis.Object.freeze([ g$here, g$here ]) +//│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 1cb2e47424..d553ed7bd4 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -62,22 +62,22 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let g1, f3, g$3; -//│ g$3 = function g$(used1, used2, g_arg) { +//│ let g3, f3, g$3; +//│ g$3 = function g$(used1, used2) { +//│ return (g_arg) => { +//│ return g3(used1, used2, g_arg) +//│ } +//│ }; +//│ g3 = function g(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; //│ return used1 + used2 //│ }; -//│ g1 = function g(used1, used2) { -//│ return (g_arg) => { -//│ return g$3(used1, used2, g_arg) -//│ } -//│ }; //│ f3 = function f(used1, unused1) { //│ let used2, unused2, foo2, tmp1, g$here; //│ used2 = unused1; //│ unused2 = 2; -//│ g$here = runtime.safeCall(g1(used1, used2)); +//│ g$here = g$3(used1, used2); //│ foo2 = g$here; //│ tmp1 = runtime.safeCall(foo2(used2)); //│ return tmp1 + unused2 @@ -92,8 +92,13 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f4, g$4; -//│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { +//│ let g4, f4, g$4; +//│ g$4 = function g$(a3, a4, a5, a2, a6, a1) { +//│ return () => { +//│ return g4(a3, a4, a5, a2, a6, a1) +//│ } +//│ }; +//│ g4 = function g(a3, a4, a5, a2, a6, a1) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = a1 + a2; //│ tmp2 = tmp1 + a3; @@ -103,7 +108,7 @@ f(1,2,3,4,5,6) //│ }; //│ f4 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp1; -//│ tmp1 = g$4(a1, a2, a3, a4, a5, a6); +//│ tmp1 = g4(a1, a2, a3, a4, a5, a6); //│ return tmp1 //│ }; //│ f4(1, 2, 3, 4, 5, 6) @@ -123,12 +128,18 @@ let x = f2().toString() f1() let y = f2().toString() x + y -//│ = "01" -//│ f1 = fun -//│ f2 = fun -//│ ret = Tuple(fun, fun) -//│ x = "0" -//│ y = "1" +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1263) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1331) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1330) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) data class Tuple(a, b) @@ -141,6 +152,18 @@ fun f(used1) = Tuple(h, i) fun g22() = used1 Tuple(g1(10), g22) +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1263) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1331) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1330) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :expect "11110110" let ret = f(1) @@ -154,16 +177,55 @@ hFun() let c = g2() let d = iFun() a.toString() + b + c + d -//│ = "11110110" -//│ a = 11 -//│ b = 1 -//│ c = 10 -//│ d = 110 -//│ g2 = fun -//│ gRet = Tuple(fun, fun) -//│ hFun = fun -//│ iFun = fun -//│ ret = Tuple(Tuple(fun, fun), fun) +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:f +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res6, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' +//│ ║ l.169: let ret = f(1) +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.146: fun f(used1) = +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.147: fun g1(used2) = +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.148: fun h() = +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.149: set used1 = 10 +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.150: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 +//│ at Runtime.checkArgs (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:615:41) +//│ at f (REPL16:1:535) +//│ at REPL31:1:180 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '"11110110"', got: 'undefined' +//│ a = undefined +//│ b = undefined +//│ c = undefined +//│ d = undefined +//│ g2 = undefined +//│ gRet = undefined +//│ hFun = undefined +//│ iFun = undefined +//│ ret = undefined // some variables mutated, some not :sjs @@ -177,17 +239,27 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f7, h$2, g$6, f$capture5; -//│ g$6 = function g$(immutable, f$capture6) { -//│ f$capture6.mutated$capture$0 = 2; -//│ return immutable + f$capture6.mutated$capture$0 +//│ let g5, h, f5, f$capture1, h$, g$5; +//│ g$5 = function g$(f$captureAAAA, immutable) { +//│ return () => { +//│ return g5(f$captureAAAA, immutable) +//│ } +//│ }; +//│ h$ = function h$(f$captureAAAA) { +//│ return () => { +//│ return h(f$captureAAAA) +//│ } +//│ }; +//│ g5 = function g(f$captureAAAA, immutable) { +//│ f$captureAAAA.mutated$capture$0 = 2; +//│ return immutable + f$captureAAAA.mutated$capture$0 //│ }; -//│ h$2 = function h$(f$capture6) { -//│ return f$capture6.mutated$capture$0 +//│ h = function h(f$captureAAAA) { +//│ return f$captureAAAA.mutated$capture$0 //│ }; -//│ (class f$capture4 { +//│ (class f$capture { //│ static { -//│ f$capture5 = this +//│ f$capture1 = this //│ } //│ constructor(mutated$capture$0) { //│ this.mutated$capture$0 = mutated$capture$0; @@ -195,15 +267,15 @@ f(1, 2, 1000) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "f$capture"]; //│ }); -//│ f7 = function f(unused, immutable, mutated) { -//│ let a1, tmp8, tmp9, capture; -//│ capture = new f$capture5(mutated); -//│ a1 = g$6(immutable, capture); -//│ tmp8 = h$2(capture); -//│ tmp9 = a1 + tmp8; -//│ return tmp9 + unused +//│ f5 = function f(unused, immutable, mutated) { +//│ let a1, tmp5, tmp6, f$capture2; +//│ f$capture2 = new f$capture1(undefined); +//│ a1 = g5(f$capture2, immutable); +//│ tmp5 = h(f$capture2); +//│ tmp6 = a1 + tmp5; +//│ return tmp6 + unused //│ }; -//│ f7(1, 2, 1000) +//│ f5(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -214,7 +286,22 @@ fun f() = fun g() = y x() f() -//│ = 2 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//│ at Runtime.checkCall (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:629:41) +//│ at REPL46:1:214 +//│ at f (REPL46:1:528) +//│ at REPL46:1:568 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' :expect 2 fun f() = @@ -224,7 +311,10 @@ fun f() = set y = 2 x() f() -//│ = 2 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '2', got: '1' +//│ = 1 :expect 2 fun f(arg) = @@ -265,44 +355,105 @@ fun g() = x f g()(1) +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = k +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, $block$res -> block$res12, member:h -> h, member:f -> f5, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, $block$res -> block$res5, member:f -> f9, member:g -> g9, member:f -> f10, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, $block$res -> block$res9, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, class:f$capture -> f$capture, member:h -> h2, member:f -> f11, member:f$capture -> f$capture1, member:g -> g10, $block$res -> block$res2, member:h$ -> h$, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$5, member:g -> g, member:f -> f, $block$res -> block$res6, member:g -> g6, $block$res -> block$res14, member:f -> f6, member:h$ -> h$2, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res10, member:g$ -> g$6, $block$res -> block$res3, member:g -> g7, member:f -> f7, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, $block$res -> block$res11, gRet -> gRet, g2 -> g21, member:g$ -> g$7, hFun -> hFun, iFun -> iFun, a -> a, member:g -> g8, b -> b, member:h -> h1, member:f -> f8, c -> c, d -> d) +//│ curThis = N +//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:scope$0$capture -> scope$0$capture, member:scope$0$capture -> scope$0$capture1, member:f$ -> f$1) +//│ curThis = S of N +//│ bindings = HashMap(x -> x, scope$0$captureAAAA -> scope$0$captureAAAA) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' +//│ ║ l.347: let k = 4 +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.347: let k = 4 +//│ ╙── ^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = scope$0$capture +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, $block$res -> block$res12, member:h -> h, member:f -> f5, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, $block$res -> block$res5, member:f -> f9, member:g -> g9, member:f -> f10, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, $block$res -> block$res9, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, class:f$capture -> f$capture, member:h -> h2, member:f -> f11, member:f$capture -> f$capture1, member:g -> g10, $block$res -> block$res2, member:h$ -> h$, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$5, member:g -> g, member:f -> f, $block$res -> block$res6, member:g -> g6, $block$res -> block$res14, member:f -> f6, member:h$ -> h$2, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res10, member:g$ -> g$6, $block$res -> block$res3, member:g -> g7, member:f -> f7, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, $block$res -> block$res11, gRet -> gRet, g2 -> g21, member:g$ -> g$7, hFun -> hFun, iFun -> iFun, a -> a, member:g -> g8, b -> b, member:h -> h1, member:f -> f8, c -> c, d -> d) +//│ curThis = N +//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:scope$0$capture -> scope$0$capture, member:scope$0$capture -> scope$0$capture1, member:f$ -> f$1) +//│ curThis = S of N +//│ bindings = HashMap(x -> x) +//│ curThis = N +//│ bindings = HashMap(k -> k) +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope$0$capture' //│ JS (unsanitized): -//│ let f14, g6, tmp8, f$1, h$4, g$capture1; -//│ h$4 = function h$(x1, k, g$capture2) { -//│ k = 5; -//│ x1 = 4; -//│ return x1 + g$capture2.y$capture$0 -//│ }; -//│ f$1 = function f$(g$capture2, x1) { -//│ let k; -//│ k = 4; -//│ g$capture2.y$capture$0 = 2; -//│ return x1 -//│ }; -//│ f14 = function f(g$capture2) { -//│ return (x1) => { -//│ return f$1(g$capture2, x1) -//│ } -//│ }; -//│ (class g$capture { +//│ let h3, f12, g11, tmp5, scope$0$capture1, f$1, h$3; +//│ (class scope$0$capture { //│ static { -//│ g$capture1 = this +//│ scope$0$capture1 = this //│ } //│ constructor(y$capture$0) { //│ this.y$capture$0 = y$capture$0; //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "g$capture"]; +//│ static [definitionMetadata] = ["class", "scope$0$capture"]; //│ }); -//│ g6 = function g() { -//│ let y1, capture, f$here; -//│ capture = new g$capture1(null); -//│ capture.y$capture$0 = 0; -//│ f$here = runtime.safeCall(f14(capture)); -//│ return f$here +//│ h$3 = function h$(scope$0$captureAAAA, x) { +//│ return () => { +//│ return h3(scope$0$captureAAAA, x) +//│ } //│ }; -//│ tmp8 = g6(); -//│ runtime.safeCall(tmp8(1)) -//│ = 1 +//│ h3 = function h(scope$0$captureAAAA, x) { +//│ k = 5; +//│ x = 4; +//│ return x + scope$0$captureAAAA.y$capture$0 +//│ }; +//│ f$1 = function f$() { +//│ return (x) => { +//│ return f12(x) +//│ } +//│ }; +//│ f12 = function f(x) { +//│ let k; +//│ k = 4; +//│ scope$0$capture.y$capture$0 = 2; +//│ return x +//│ }; +//│ g11 = function g() { +//│ let y, scope$0$capture2; +//│ scope$0$capture2 = new scope$0$capture1(undefined); +//│ scope$0$capture2.y$capture$0 = 0; +//│ return f12 +//│ }; +//│ tmp5 = g11(); +//│ runtime.safeCall(tmp5(1)) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: scope$0$capture is not defined +//│ at f (REPL61:1:1136) +//│ at REPL61:1:1456 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) :expect 2 fun f() = @@ -330,7 +481,56 @@ fun f(x, y, z) = set z = 3 g f(0, 0, 0)() -//│ = 6 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = f$capture +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, $block$res -> block$res7, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ curThis = S of N +//│ bindings = HashMap($args -> args) +//│ curThis = N +//│ bindings = HashMap() +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$capture' +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = f$capture +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, $block$res -> block$res7, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ curThis = S of N +//│ bindings = HashMap($args -> args) +//│ curThis = N +//│ bindings = HashMap() +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$capture' +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: f$capture is not defined +//│ at h (REPL68:1:1067) +//│ at g (REPL68:1:874) +//│ at REPL68:1:274 +//│ at REPL68:1:1973 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '6', got: 'undefined' fun f(x, cond) = set x = 1 @@ -349,6 +549,28 @@ fun f() = f() x f() +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = x +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:f -> f15, member:g -> g7, member:f -> f7, $block$res -> block$res18, member:lambda -> lambda, member:lambda -> lambda1, member:lambda$ -> lambda$, $block$res -> block$res11, member:lambda$ -> lambda$1, member:g$ -> g$7, member:f -> f16, member:g -> g8, member:g -> g14, member:h -> h1, member:f -> f17, member:f -> f8, $block$res -> block$res7, $block$res -> block$res19, member:f$ -> f$2, member:g$ -> g$12, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ curThis = S of N +//│ bindings = HashMap($args -> args) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' +//│ ║ l.546: let x = 1 +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.546: let x = 1 +//│ ╙── ^ //│ = 1 let n = 0 @@ -367,6 +589,18 @@ fun f(es, vs) = class A with val fld = v' new A +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1261) +//│ at: hkmc2.Lifter.$anonfun$57(Lifter.scala:1296) +//│ at: scala.collection.immutable.List.map(List.scala:236) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1296) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1320) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:923) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:930) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:629) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) // try to avoid creating multiple closures @@ -385,30 +619,26 @@ fun f(x) = set y = 2 [g, g] //│ JS (unsanitized): -//│ let g12, f23, g$14; -//│ g$14 = function g$(y1) { -//│ return y1 -//│ }; -//│ g12 = function g(y1) { +//│ let g17, f20, g$15; +//│ g$15 = function g$(y) { //│ return () => { -//│ return g$14(y1) +//│ return g17(y) //│ } //│ }; -//│ f23 = function f(x1) { -//│ let y1, scrut, g$here; -//│ scrut = x1 < 0; +//│ g17 = function g(y) { +//│ return y +//│ }; +//│ f20 = function f(x) { +//│ let y, scrut, g$here; +//│ scrut = x < 0; //│ if (scrut === true) { -//│ y1 = 1; -//│ g$here = runtime.safeCall(g12(y1)); +//│ y = 1; +//│ g$here = g$15(y); //│ return globalThis.Object.freeze([ //│ g$here, //│ g$here //│ ]) -//│ } else { -//│ y1 = 2; -//│ g$here = runtime.safeCall(g12(y1)); -//│ return globalThis.Object.freeze([ g$here, g$here ]) -//│ } +//│ } else { y = 2; g$here = g$15(y); return globalThis.Object.freeze([ g$here, g$here ]) } //│ }; :sjs @@ -418,33 +648,22 @@ fun f(x) = set x += 1 [a, g] //│ JS (unsanitized): -//│ let g13, f24, g$15, f$capture17; -//│ g$15 = function g$(f$capture18) { -//│ return f$capture18.x$capture$0 -//│ }; -//│ g13 = function g(f$capture18) { +//│ let g18, f21, g$16; +//│ g$16 = function g$(x) { //│ return () => { -//│ return g$15(f$capture18) +//│ return g18(x) //│ } //│ }; -//│ (class f$capture16 { -//│ static { -//│ f$capture17 = this -//│ } -//│ constructor(x$capture$0) { -//│ this.x$capture$0 = x$capture$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "f$capture"]; -//│ }); -//│ f24 = function f(x1) { -//│ let a1, tmp11, capture, g$here; -//│ capture = new f$capture17(x1); -//│ g$here = runtime.safeCall(g13(capture)); +//│ g18 = function g(x) { +//│ return x +//│ }; +//│ f21 = function f(x) { +//│ let a1, tmp8, g$here; +//│ g$here = g$16(x); //│ a1 = g$here; -//│ tmp11 = capture.x$capture$0 + 1; -//│ capture.x$capture$0 = tmp11; -//│ g$here = runtime.safeCall(g13(capture)); +//│ tmp8 = x + 1; +//│ x = tmp8; +//│ g$here = g$16(x); //│ return globalThis.Object.freeze([ a1, g$here ]) //│ }; From 29d8c6c56567d91a335fe179d5749d22a9da0b8d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 15 Jan 2026 22:55:35 +0800 Subject: [PATCH 03/18] support loops --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 65 +++++-------- .../main/scala/hkmc2/codegen/ScopeData.scala | 15 ++- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 3 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 94 ++++++++++++------- 4 files changed, 98 insertions(+), 79 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index abe69cd190..1a16541a10 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -112,7 +112,7 @@ object Lifter: * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. */ -class Lifter(blk: Block)(using State, Raise): +class Lifter(topLevelBlk: Block)(using State, Raise): import Lifter.* /** @@ -817,14 +817,14 @@ class Lifter(blk: Block)(using State, Raise): end liftOutDefnCont given ignoredScopes: IgnoredScopes = IgnoredScopes(N) - val data = ScopeData(blk) + val data = ScopeData(topLevelBlk) val metadata = data.root.children.foldLeft(LifterMetadata.empty)(_ ++ createMetadata(_)) def asDSym(s: ClsSym | ModuleOrObjSym): DefinitionSymbol[?] = s val ignored: Set[ScopedInfo] = metadata.unliftable.map(asDSym) ignoredScopes.ignored = S(ignored) - val usedVars = UsedVarAnalyzer(blk, data) + val usedVars = UsedVarAnalyzer(topLevelBlk, data) // for debugging def printMap[T, V](m: Map[T, V]) = @@ -836,7 +836,7 @@ class Lifter(blk: Block)(using State, Raise): println(v) println(")") - /* + println("accessesShallow") printMap(usedVars.shallowAccesses) println("accesses") @@ -844,7 +844,7 @@ class Lifter(blk: Block)(using State, Raise): printMap(usedVars.accessMapWithIgnored) println("usedVars") printMap(usedVars.reqdCaptures) - */ + def isIgnored(d: Defn) = d match case f: FunDefn => ignored.contains(f.dSym) @@ -868,7 +868,7 @@ class Lifter(blk: Block)(using State, Raise): */ def createCaptureCls(s: ScopedObject) : (ClsLikeDefn, List[(Local, TermSymbol)]) = - val nme = s.nme + "$capture" + val nme = "Capture$" + s.nme val clsSym = ClassSymbol( Tree.DummyTypeDef(syntax.Cls), @@ -881,7 +881,7 @@ class Lifter(blk: Block)(using State, Raise): val sortedVars = cap.toArray.sortBy(_.uid).map: sym => val id = fresh.make - val nme = sym.nme + "$capture$" + id + val nme = sym.nme + "$" + id val ident = new Tree.Ident(nme) val varSym = VarSymbol(ident) @@ -1085,7 +1085,7 @@ class Lifter(blk: Block)(using State, Raise): * A rewritten scope with a generic VarSymbol capture symbol. */ sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: - lazy val captureSym = VarSymbol(Tree.Ident(this.obj.nme + "$capture")) + lazy val captureSym = VarSymbol(Tree.Ident(this.obj.nme + "$cap")) override lazy val capturePath = captureSym.asPath protected def addCaptureSym(b: Block): Block = @@ -1113,13 +1113,22 @@ class Lifter(blk: Block)(using State, Raise): override def rewriteImpl: LifterResult[Block] = val rewriter = new BlockRewriter - val rmved = removeLiftedScopes(obj) - val (syms, rewritten) = rmved match - case s: Scoped => (s.syms.toSet, rewriter.rewrite(s.body)) - case b => (Set.empty, rewriter.rewrite(b)) + // Remove symbols belonging to lifted scopes + val liftedChildSyms = node.children.collect: + case s @ ScopeNode(obj = l: ScopedObject.Liftable[?]) if s.isLifted => l.defn.sym + + val (syms, rewritten) = (obj.block.syms.toSet -- liftedChildSyms, rewriter.rewrite(obj.block.body)) val withCapture = addCaptureSym(rewritten) LifterResult(Scoped(syms, withCapture), rewriter.extraDefns.toList) + class RewrittenLoop(override val obj: ScopedObject.Loop)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: + override def rewriteImpl: LifterResult[Block] = + val rewriter = new BlockRewriter + + val rewritten = rewriter.rewrite(obj.body) + val withCapture = addCaptureSym(rewritten) + LifterResult(withCapture, rewriter.extraDefns.toList) + class RewrittenFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends RewrittenScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: override def rewriteImpl: LifterResult[FunDefn] = val rewriter = new BlockRewriter @@ -1134,7 +1143,7 @@ class Lifter(blk: Block)(using State, Raise): .toMap private val capSymsMap_ : Map[ScopedInfo, VarSymbol] = reqCaptures.map: i => val nme = data.getNode(i).obj.nme - i -> VarSymbol(Tree.Ident(nme + "$capture")) + i -> VarSymbol(Tree.Ident(nme + "$cap")) .toMap override lazy val capturesOrder: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) @@ -1227,34 +1236,6 @@ class Lifter(blk: Block)(using State, Raise): def rewriteImpl: LifterResult[FunDefn] = val LifterResult(lifted, extra) = mkFlattenedDefn LifterResult(lifted, mkAuxDefn :: extra) - - - /** - * Removes nested scopes that are to be lifted. - * - * @param s The scoped object whose nested scopes are to be removed. - * @return The scoped object's contents with its nested scopes removed. - */ - def removeLiftedScopes[T](s: TScopedObject[T]): T = s match - case ScopedObject.Top(b) => lastWords("Tried to remove nested scopes from the top level scope.") - case ScopedObject.Class(cls) => - val (preCtorNew, defns1) = cls.preCtor.extractDefns(isIgnored) - val (ctorNew, defns2) = cls.ctor.extractDefns(isIgnored) - cls.copy(ctor = ctorNew, preCtor = preCtorNew) - case ScopedObject.Companion(comp, par) => - val (ctorNew, defns) = comp.ctor.extractDefns(isIgnored) - comp.copy(ctor = ctorNew) - case ScopedObject.ClassCtor(cls) => () - case ScopedObject.Func(fun, isMethod) => - val (bodyNew, defns) = fun.body.extractDefns(isIgnored) - fun.copy(body = bodyNew)(fun.forceTailRec) - case ScopedObject.Loop(sym, block) => - val (blkNew, defns) = block.extractDefns(isIgnored) - blkNew - case ScopedObject.ScopedBlock(uid, block) => - val (blkNew, defns) = block.body.extractDefns(isIgnored) - val bms = defns.map(_.sym) - block.copy(block.syms.toSet -- bms, blkNew) private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") @@ -1264,7 +1245,7 @@ class Lifter(blk: Block)(using State, Raise): case o: ScopedObject.Func => if s.isLifted && !s.isTopLevel then LiftedFunc(o) else RewrittenFunc(o) - case o: ScopedObject.Loop => ??? + case o: ScopedObject.Loop => RewrittenLoop(o) case o: ScopedObject.ScopedBlock => RewrittenScopedBlock(o) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index 68994bc37e..a415e8b723 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -58,7 +58,7 @@ object ScopeData: case ClassCtor(cls) => cls.isym.nme // should be unused case Func(fun, isMethod) => fun.dSym.nme case Loop(sym, block) => "loop$" + sym.uid.toString() - case ScopedBlock(uid, block) => "scope$" + uid + case ScopedBlock(uid, block) => "scope" + uid // Locals defined by a scoped object. lazy val definedLocals: Set[Local] = this match @@ -190,11 +190,11 @@ object ScopeData: case Some(_: ScopedObject.Companion) => false // there is no need to lift objects nested inside a module case _ => obj match - case _: ScopedObject.ScopedBlock => false // case _: ScopedObject.Companion => false // case c: ScopedObject.Class if c.cls.companion.isDefined => false - case ScopedObject.Func(isMethod = true) => false case _ if ignored.contains(obj.toInfo) => false + case ScopedObject.Func(isMethod = true) => false + case _: ScopedObject.Loop | _: ScopedObject.ClassCtor | _: ScopedObject.ScopedBlock | _: ScopedObject.Companion => false case _ => true lazy val isTopLevel: Bool = parent match @@ -264,14 +264,19 @@ class ScopeData(b: Block)(using State, IgnoredScopes): def getNode(defn: FunDefn): ScopeNode = getNode(defn.dSym) def getUID(blk: Scoped): ScopeUID = if scopedMap.containsKey(blk) then scopedMap.get(blk) - else lastWords("getUID: key not found") + else + println("key not found:") + println(blk) + lastWords("getUID: key not found") def getNode(blk: Scoped): ScopeNode = getNode(getUID(blk)) // From the input block or definition, traverses until a function, class or new scoped block is found and appends them. class ScopeFinder extends BlockTraverserShallow: var objs: List[ScopedObject] = Nil override def applyBlock(b: Block): Unit = b match case s: Scoped => - objs ::= ScopedObject.ScopedBlock(fresh.make, s) + val id = fresh.make + println("fresh: " + id) + objs ::= ScopedObject.ScopedBlock(id, s) case l: Label if l.loop => objs ::= ScopedObject.Loop(l.label, l.body) case _ => super.applyBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 27db68d747..e2cd092317 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -208,6 +208,9 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // "ignore" `c` in the sense that it does not need to capture `s`'s scoped object's variables, nor does // it require the current scoped object to create a capture class for its accessed variables. def isIgnored(c: ScopedInfo) = + println("in " + s.obj.toInfo + ":") + println(" " + c.toString + " is ignored: " + s.inSubtree(scopeData.getNode(c).firstLiftedParent.toInfo)) + println(" first lifted parent of " + c + ": " + scopeData.getNode(c).firstLiftedParent.toInfo) s.inSubtree(scopeData.getNode(c).firstLiftedParent.toInfo) // All objects in the same scc must have at least the same accesses as each other diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 588a2e4b4d..ad7aab2945 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -5,45 +5,75 @@ // :elt :global -// :d -// :todo - +:d +:todo +:noSanityCheck :lift -:sjs -fun f(x, y) = - fun g(a)(z) = - set x += 1 - y - [g, g] -//│ JS (unsanitized): -//│ let g, f, f$capture1, g$; -//│ g$ = function g$(f$captureAAAA, y) { -//│ return (a) => { -//│ return g(f$captureAAAA, y, a) -//│ } -//│ }; -//│ g = function g(f$captureAAAA, y, a) { -//│ return (z) => { -//│ let tmp; -//│ tmp = f$captureAAAA.x$capture$0 + 1; -//│ f$captureAAAA.x$capture$0 = tmp; -//│ return y +:ssjs +:noTailRec +fun f(x, y, i) = + let x = [] + while i < 10 do + let x = 2 + fun bruh() = set x += 1 + bruh() + bruh() + set i += 1 + i +//│ Elab: { fun member:f‹687›(x‹688›, y‹689›, i‹690›) = { let x‹692›; x‹692› = []; while { let scrut = builtin:<‹33›#0(i‹690›#666, 10); $scrut‹694›#0 is true then { let x‹695›; x‹695› = 2; fun member:bruh‹686›() = x‹695›#666 := builtin:+‹48›#0(x‹695›#666, 1); member:bruh‹686›#666(); member:bruh‹686›#666(); i‹690›#666 := builtin:+‹48›#1(i‹690›#666, 1) }; else () }; i‹690›#666 }; } +//│ JS: +//│ let bruh, f, Capture$scope11, bruh$; +//│ bruh$ = function bruh$(scope1$cap) { +//│ return () => { +//│ return bruh(scope1$cap) //│ } //│ }; -//│ (class f$capture { +//│ (class Capture$scope1 { //│ static { -//│ f$capture1 = this +//│ Capture$scope11 = this //│ } -//│ constructor(x$capture$0) { -//│ this.x$capture$0 = x$capture$0; +//│ constructor(x$0) { +//│ this.x$0 = x$0; //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "f$capture"]; +//│ static [definitionMetadata] = ["class", "Capture$scope1"]; //│ }); -//│ f = function f(x, y) { -//│ let f$capture2, g$here; -//│ f$capture2 = new f$capture1(undefined); -//│ g$here = g$(f$capture2, y); -//│ return globalThis.Object.freeze([ g$here, g$here ]) +//│ bruh$ = function bruh$(scope1$cap) { +//│ return () => { +//│ return bruh(scope1$cap) +//│ } +//│ }; +//│ bruh = function bruh(scope1$cap) { +//│ let tmp; +//│ tmp = scope1$cap.x$0 + 1; +//│ scope1$cap.x$0 = tmp; +//│ return runtime.Unit +//│ }; +//│ f = function f(x, y, i) { +//│ let x1, tmp; +//│ x1 = globalThis.Object.freeze([]); +//│ lbl: while (true) { +//│ let scrut, x2, tmp1, tmp2, tmp3, scope1$cap; +//│ scope1$cap = new Capture$scope11(undefined); +//│ scrut = i < 10; +//│ if (scrut === true) { +//│ bruh = function bruh(scope1$cap1) { +//│ let tmp4; +//│ tmp4 = scope1$cap1.x$0 + 1; +//│ scope1$cap1.x$0 = tmp4; +//│ return runtime.Unit +//│ }; +//│ scope1$cap.x$0 = 2; +//│ tmp1 = bruh(scope1$cap); +//│ tmp2 = bruh(scope1$cap); +//│ tmp3 = i + 1; +//│ i = tmp3; +//│ tmp = runtime.Unit; +//│ continue lbl +//│ } else { tmp = runtime.Unit; } +//│ break; +//│ } +//│ return i //│ }; +//│ block$res1 = undefined; From 4d76a88c137489e7a54eb0b632393e96e810edd0 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 15 Jan 2026 23:56:24 +0800 Subject: [PATCH 04/18] more correct and precise analysis --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 4 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 70 ++-- hkmc2/shared/src/test/mlscript/HkScratch.mls | 78 ++-- .../src/test/mlscript/lifter/FunInFun.mls | 361 +++++++++++------- 4 files changed, 279 insertions(+), 234 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 1a16541a10..53f68ef102 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -875,7 +875,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): Tree.Ident(nme) ) - val (_, cap) = usedVars.reqdCaptures(s.toInfo) + val cap = usedVars.reqdCaptures(s.toInfo) val fresh = FreshInt() @@ -960,7 +960,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): sealed abstract class RewrittenScope[T](val obj: TScopedObject[T]): val node = obj.node.get - protected val (_, thisCapturedLocals) = usedVars.reqdCaptures(obj.toInfo) + protected val thisCapturedLocals = usedVars.reqdCaptures(obj.toInfo) val hasCapture = !thisCapturedLocals.isEmpty // These are lazy, because we don't necessarily need a captrue diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index e2cd092317..ba9bc5df18 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -248,20 +248,20 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val accessMapWithIgnored = m1.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) val accessMap = m2.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) - private def reqdCaptureLocals(s: ScopeNode): Map[ScopedInfo, (Set[Local], Set[Local])] = + private def reqdCaptureLocals(s: ScopeNode): Map[ScopedInfo, Set[Local]] = val (blk, extraMtds) = s.obj match case ScopedObject.Top(b) => lastWords("reqdCaptureLocals called on top block") - case ScopedObject.ClassCtor(cls) => return Map.empty + (s.obj.toInfo -> (Set.empty, Set.empty)) + case ScopedObject.ClassCtor(cls) => return Map.empty + (s.obj.toInfo -> Set.empty) case ScopedObject.Class(cls) => (Begin(cls.preCtor, cls.ctor), cls.methods) case ScopedObject.Companion(comp, _) => (comp.ctor, comp.methods) case ScopedObject.Func(fun, _) => (fun.body, Nil) case ScopedObject.ScopedBlock(uid, block) => (block, Nil) case ScopedObject.Loop(sym, block) => (block, Nil) - // traverse all scoped blocks + // traverse all scoped blocks and loops val nexts: Buffer[ScopeNode] = Buffer.empty def findNodes(s: ScopeNode): List[ScopeNode] = s :: s.children.flatMap: - case c @ ScopeNode(obj = obj: ScopedObject.ScopedBlock) => findNodes(c) + case c @ ScopeNode(obj = obj: (ScopedObject.ScopedBlock | ScopedObject.Loop)) => findNodes(c) case c => nexts.addOne(c) List.empty @@ -269,7 +269,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val locals = nodes.flatMap(_.obj.definedLocals).toSet - val (read, cap) = reqdCaptureLocalsBlk(blk, nexts.toList, locals) + val cap = reqdCaptureLocalsBlk(blk, nexts.toList, locals) // Variables mutated by a lifted child of a class methods requires a capture val additional = extraMtds @@ -277,33 +277,24 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes scopeData.getNode(mtd).liftedChildNodes.map(x => x.obj.toInfo) .foldLeft(AccessInfo.empty): case (acc, value) => acc ++ accessMap(value) - val (newRead, newCap) = (read ++ additional.accessed, cap ++ additional.mutated) + val newCap = cap ++ additional.mutated - val (usedVarsL, mutatedVarsL) = nexts.map: node => - val a = accessMap(node.obj.toInfo) - (a.accessed, a.mutated) - .unzip - - val usedVars = usedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) - val mutatedVars = mutatedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) - - val cur: Map[ScopedInfo, (Set[Local], Set[Local])] = nodes.map: n => - n.obj.toInfo -> ( - newRead.intersect(usedVars).intersect(n.obj.definedLocals), - newCap.intersect(mutatedVars).intersect(n.obj.definedLocals) - ) + val cur: Map[ScopedInfo, Set[Local]] = nodes.map: n => + n.obj.toInfo -> newCap.intersect(n.obj.definedLocals) .toMap nexts.foldLeft(cur): case (mp, acc) => mp ++ reqdCaptureLocals(acc) // readers-mutators analysis - private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], thisVars: Set[Local]): (Set[Local], Set[Local]) = + private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], thisVars: Set[Local]): Set[Local] = val scopeInfos: Map[ScopedInfo, ScopeNode] = nextNodes.map(node => node.obj.toInfo -> node).toMap case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local]) - - def go(b: Block, reqCapture_ : Set[Local], hasReader_ : Set[Local], hasMutator_ : Set[Local]): CaptureInfo = + + // isLinear denotes whether the control flow is linear, i.e. whether `b` could be executed more than once assuming that + // the input block in `reqdCaptureLocalsBlk` is only executed once. + def go(b: Block, reqCapture_ : Set[Local], hasReader_ : Set[Local], hasMutator_ : Set[Local])(using isLinear: Bool): CaptureInfo = var reqCapture = reqCapture_ var hasReader = hasReader_ var hasMutator = hasMutator_ @@ -313,7 +304,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes hasReader ++= c.hasReader hasMutator ++= c.hasMutator - def rec(blk: Block) = + def rec(blk: Block)(using isLinear: Bool) = go(blk, reqCapture, hasReader, hasMutator) new BlockTraverserShallow: @@ -322,8 +313,11 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // Note that we traverse directly into scoped blocks without using handleCalledScope case l: Label if l.loop => - handleCalledScope(l.label) + rec(l.body)(using isLinear = false) |> merge + applyBlock(l.rest) case Assign(lhs, rhs, rest) => + println("assign: " + lhs + " = " + rhs) + println("has readers: " + hasReader) applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs applyBlock(rest) @@ -364,11 +358,13 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case ScopedObject.Func(_, true) => false case _ => true - // this not a naked reference. if it's a ref to a class, this can only ever create once instance - // so the "one writer" rule applies + // This not a naked reference. If it's a ref to a class, this can only ever create once instance + // so the "one writer" rule applies. + // However, if the control flow is not linear, we are forced to add all the mutated variables for l <- muts do - if hasReader.contains(l) || hasMutator.contains(l) then + if hasReader.contains(l) || hasMutator.contains(l) || !isLinear then reqCapture += l + hasReader += l hasMutator += l for l <- reads do if hasMutator.contains(l) then @@ -405,6 +401,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(d) val muts = muted.intersect(thisVars) val reads = accessed.intersect(thisVars) -- muts + println("naked ref: " + p) // this is a naked reference, we assume things it mutates always needs a capture for l <- muts do reqCapture += l @@ -412,6 +409,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes for l <- reads do if hasMutator.contains(l) then reqCapture += l + println("has reader: " + l) hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily @@ -434,24 +432,16 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case _ => super.applyDefn(defn) CaptureInfo(reqCapture, hasReader, hasMutator) + + val reqCapture = go(b, Set.empty, Set.empty, Set.empty)(using isLinear = true).reqCapture + reqCapture.intersect(thisVars) - val (usedVarsL, mutatedVarsL) = scopeInfos.map: - case (info, node) => - val a = accessMap(info).intersectLocals(thisVars) - (a.accessed, a.mutated) - .unzip - val usedVars = usedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) - val mutatedVars = mutatedVarsL.foldLeft[Set[Local]](Set.empty)(_ ++ _) - val reqCapture = go(b, Set.empty, Set.empty, Set.empty).reqCapture.intersect(mutatedVars) - - (usedVars, reqCapture) - - val reqdCaptures: Map[ScopedInfo, (Set[Local], Set[Local])] = scopeData.root.children.foldLeft(Map.empty): + val reqdCaptures: Map[ScopedInfo, Set[Local]] = scopeData.root.children.foldLeft(Map.empty): case (acc, node) => acc ++ reqdCaptureLocals(node) // For local inside a capture, finds the node to which this local belongs. val capturesMap = for - case (info -> (_, reqCap)) <- reqdCaptures + case (info -> reqCap) <- reqdCaptures s <- reqCap yield s -> info diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index ad7aab2945..4169f2b613 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -8,67 +8,27 @@ :d :todo -:noSanityCheck :lift :ssjs -:noTailRec -fun f(x, y, i) = - let x = [] +:noSanityCheck +fun f() = + let i = 0 while i < 10 do - let x = 2 - fun bruh() = set x += 1 - bruh() - bruh() set i += 1 i -//│ Elab: { fun member:f‹687›(x‹688›, y‹689›, i‹690›) = { let x‹692›; x‹692› = []; while { let scrut = builtin:<‹33›#0(i‹690›#666, 10); $scrut‹694›#0 is true then { let x‹695›; x‹695› = 2; fun member:bruh‹686›() = x‹695›#666 := builtin:+‹48›#0(x‹695›#666, 1); member:bruh‹686›#666(); member:bruh‹686›#666(); i‹690›#666 := builtin:+‹48›#1(i‹690›#666, 1) }; else () }; i‹690›#666 }; } +f() +//│ Elab: { fun member:f‹686›() = { let i‹688›; i‹688› = 0; while { let scrut = builtin:<‹33›#0(i‹688›#666, 10); $scrut‹690›#0 is true then i‹688›#666 := builtin:+‹48›#0(i‹688›#666, 1); else () }; i‹688›#666 }; member:f‹686›#666() } //│ JS: -//│ let bruh, f, Capture$scope11, bruh$; -//│ bruh$ = function bruh$(scope1$cap) { -//│ return () => { -//│ return bruh(scope1$cap) -//│ } -//│ }; -//│ (class Capture$scope1 { -//│ static { -//│ Capture$scope11 = this -//│ } -//│ constructor(x$0) { -//│ this.x$0 = x$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Capture$scope1"]; -//│ }); -//│ bruh$ = function bruh$(scope1$cap) { -//│ return () => { -//│ return bruh(scope1$cap) -//│ } -//│ }; -//│ bruh = function bruh(scope1$cap) { -//│ let tmp; -//│ tmp = scope1$cap.x$0 + 1; -//│ scope1$cap.x$0 = tmp; -//│ return runtime.Unit -//│ }; -//│ f = function f(x, y, i) { -//│ let x1, tmp; -//│ x1 = globalThis.Object.freeze([]); +//│ let f; +//│ f = function f() { +//│ let i, tmp; +//│ i = 0; //│ lbl: while (true) { -//│ let scrut, x2, tmp1, tmp2, tmp3, scope1$cap; -//│ scope1$cap = new Capture$scope11(undefined); +//│ let scrut, tmp1; //│ scrut = i < 10; //│ if (scrut === true) { -//│ bruh = function bruh(scope1$cap1) { -//│ let tmp4; -//│ tmp4 = scope1$cap1.x$0 + 1; -//│ scope1$cap1.x$0 = tmp4; -//│ return runtime.Unit -//│ }; -//│ scope1$cap.x$0 = 2; -//│ tmp1 = bruh(scope1$cap); -//│ tmp2 = bruh(scope1$cap); -//│ tmp3 = i + 1; -//│ i = tmp3; +//│ tmp1 = i + 1; +//│ i = tmp1; //│ tmp = runtime.Unit; //│ continue lbl //│ } else { tmp = runtime.Unit; } @@ -76,4 +36,16 @@ fun f(x, y, i) = //│ } //│ return i //│ }; -//│ block$res1 = undefined; +//│ block$res1 = f(); +//│ undefined +//│ = 10 + +:expect 2 +fun f() = + let x = g + let y = 2 + fun g() = y + x() +f() +//│ Elab: { fun member:f‹701›() = { let x‹703›; x‹703› = member:g‹700›#666; let y‹704›; y‹704› = 2; fun member:g‹700›() = y‹704›#666; x‹703›#666() }; member:f‹701›#666() } +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index d553ed7bd4..3a80147933 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -63,18 +63,28 @@ fun f(used1, unused1) = f(1, 2) //│ JS (unsanitized): //│ let g3, f3, g$3; -//│ g$3 = function g$(used1, used2) { +//│ g$3 = function g$(used2, used1) { //│ return (g_arg) => { -//│ return g3(used1, used2, g_arg) +//│ return g3(used2, used1, g_arg) //│ } //│ }; -//│ g3 = function g(used1, used2, g_arg) { +//│ g$3 = function g$(used2, used1) { +//│ return (g_arg) => { +//│ return g3(used2, used1, g_arg) +//│ } +//│ }; +//│ g3 = function g(used2, used1, g_arg) { //│ let used3; //│ used3 = 2; //│ return used1 + used2 //│ }; //│ f3 = function f(used1, unused1) { //│ let used2, unused2, foo2, tmp1, g$here; +//│ g3 = function g(used21, used11, g_arg) { +//│ let used3; +//│ used3 = 2; +//│ return used11 + used21 +//│ }; //│ used2 = unused1; //│ unused2 = 2; //│ g$here = g$3(used1, used2); @@ -93,12 +103,17 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let g4, f4, g$4; -//│ g$4 = function g$(a3, a4, a5, a2, a6, a1) { +//│ g$4 = function g$(a3, a6, a2, a4, a5, a1) { //│ return () => { -//│ return g4(a3, a4, a5, a2, a6, a1) +//│ return g4(a3, a6, a2, a4, a5, a1) //│ } //│ }; -//│ g4 = function g(a3, a4, a5, a2, a6, a1) { +//│ g$4 = function g$(a3, a6, a2, a4, a5, a1) { +//│ return () => { +//│ return g4(a3, a6, a2, a4, a5, a1) +//│ } +//│ }; +//│ g4 = function g(a3, a6, a2, a4, a5, a1) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = a1 + a2; //│ tmp2 = tmp1 + a3; @@ -108,6 +123,14 @@ f(1,2,3,4,5,6) //│ }; //│ f4 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp1; +//│ g4 = function g(a31, a61, a21, a41, a51, a11) { +//│ let tmp2, tmp3, tmp4, tmp5; +//│ tmp2 = a11 + a21; +//│ tmp3 = tmp2 + a31; +//│ tmp4 = tmp3 + a41; +//│ tmp5 = tmp4 + a51; +//│ return tmp5 + a61 +//│ }; //│ tmp1 = g4(a1, a2, a3, a4, a5, a6); //│ return tmp1 //│ }; @@ -131,10 +154,10 @@ x + y //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1263) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1331) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1244) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1304) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1330) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1303) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -155,10 +178,10 @@ fun f(used1) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1263) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1331) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1244) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1304) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1330) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1303) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -186,26 +209,26 @@ a.toString() + b + c + d //│ _2 = Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res6, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, ret -> ret, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, $block$res -> block$res7, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:g$ -> g$4, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res, $block$res -> block$res5, member:bar -> bar, member:foo -> foo, member:g$ -> g$2, $block$res -> block$res1, member:bar$ -> bar$, member:g -> g3, member:bar -> bar1, member:f -> f3, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, member:Predef -> Predef, $block$res -> block$res6, member:g$ -> g$3, $block$res -> block$res3, member:g$ -> g$, member:g -> g4, member:f -> f4, member:g -> g1, member:f -> f1) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//│ ║ l.169: let ret = f(1) +//│ ║ l.192: let ret = f(1) //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.146: fun f(used1) = +//│ ║ l.169: fun f(used1) = //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.147: fun g1(used2) = +//│ ║ l.170: fun g1(used2) = //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.148: fun h() = +//│ ║ l.171: fun h() = //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.149: set used1 = 10 +//│ ║ l.172: set used1 = 10 //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.150: ... (more lines omitted) ... +//│ ║ l.173: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 //│ at Runtime.checkArgs (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:615:41) -//│ at f (REPL16:1:535) +//│ at f (REPL16:1:819) //│ at REPL31:1:180 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) @@ -239,39 +262,56 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let g5, h, f5, f$capture1, h$, g$5; -//│ g$5 = function g$(f$captureAAAA, immutable) { +//│ let g5, h, f5, Capture$f1, h$, g$5; +//│ h$ = function h$(f$cap) { //│ return () => { -//│ return g5(f$captureAAAA, immutable) +//│ return h(f$cap) //│ } //│ }; -//│ h$ = function h$(f$captureAAAA) { +//│ g$5 = function g$(f$cap, immutable) { //│ return () => { -//│ return h(f$captureAAAA) +//│ return g5(f$cap, immutable) //│ } //│ }; -//│ g5 = function g(f$captureAAAA, immutable) { -//│ f$captureAAAA.mutated$capture$0 = 2; -//│ return immutable + f$captureAAAA.mutated$capture$0 +//│ g$5 = function g$(f$cap, immutable) { +//│ return () => { +//│ return g5(f$cap, immutable) +//│ } +//│ }; +//│ h$ = function h$(f$cap) { +//│ return () => { +//│ return h(f$cap) +//│ } +//│ }; +//│ g5 = function g(f$cap, immutable) { +//│ f$cap.mutated$0 = 2; +//│ return immutable + f$cap.mutated$0 //│ }; -//│ h = function h(f$captureAAAA) { -//│ return f$captureAAAA.mutated$capture$0 +//│ h = function h(f$cap) { +//│ return f$cap.mutated$0 //│ }; -//│ (class f$capture { +//│ (class Capture$f { //│ static { -//│ f$capture1 = this +//│ Capture$f1 = this //│ } -//│ constructor(mutated$capture$0) { -//│ this.mutated$capture$0 = mutated$capture$0; +//│ constructor(mutated$0) { +//│ this.mutated$0 = mutated$0; //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "f$capture"]; +//│ static [definitionMetadata] = ["class", "Capture$f"]; //│ }); //│ f5 = function f(unused, immutable, mutated) { -//│ let a1, tmp5, tmp6, f$capture2; -//│ f$capture2 = new f$capture1(undefined); -//│ a1 = g5(f$capture2, immutable); -//│ tmp5 = h(f$capture2); +//│ let a1, tmp5, tmp6, f$cap; +//│ f$cap = new Capture$f1(undefined); +//│ g5 = function g(f$cap1, immutable1) { +//│ f$cap1.mutated$0 = 2; +//│ return immutable1 + f$cap1.mutated$0 +//│ }; +//│ h = function h(f$cap1) { +//│ return f$cap1.mutated$0 +//│ }; +//│ a1 = g5(f$cap, immutable); +//│ tmp5 = h(f$cap); //│ tmp6 = a1 + tmp5; //│ return tmp6 + unused //│ }; @@ -286,22 +326,7 @@ fun f() = fun g() = y x() f() -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. -//│ at Runtime.checkCall (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:629:41) -//│ at REPL46:1:214 -//│ at f (REPL46:1:528) -//│ at REPL46:1:568 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 :expect 2 fun f() = @@ -311,10 +336,7 @@ fun f() = set y = 2 x() f() -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '2', got: '1' -//│ = 1 +//│ = 2 :expect 2 fun f(arg) = @@ -367,24 +389,24 @@ g()(1) //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, $block$res -> block$res12, member:h -> h, member:f -> f5, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, $block$res -> block$res5, member:f -> f9, member:g -> g9, member:f -> f10, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, $block$res -> block$res9, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, class:f$capture -> f$capture, member:h -> h2, member:f -> f11, member:f$capture -> f$capture1, member:g -> g10, $block$res -> block$res2, member:h$ -> h$, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$5, member:g -> g, member:f -> f, $block$res -> block$res6, member:g -> g6, $block$res -> block$res14, member:f -> f6, member:h$ -> h$2, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res10, member:g$ -> g$6, $block$res -> block$res3, member:g -> g7, member:f -> f7, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, $block$res -> block$res11, gRet -> gRet, g2 -> g21, member:g$ -> g$7, hFun -> hFun, iFun -> iFun, a -> a, member:g -> g8, b -> b, member:h -> h1, member:f -> f8, c -> c, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) //│ curThis = N -//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:scope$0$capture -> scope$0$capture, member:scope$0$capture -> scope$0$capture1, member:f$ -> f$1) +//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) //│ curThis = S of N -//│ bindings = HashMap(x -> x, scope$0$captureAAAA -> scope$0$captureAAAA) +//│ bindings = HashMap(x -> x, scope0$cap -> scope0$cap) //│ curThis = N //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' -//│ ║ l.347: let k = 4 +//│ ║ l.369: let k = 4 //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.347: let k = 4 +//│ ║ l.369: let k = 4 //│ ╙── ^ //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) //│ FAILURE INFO: Tuple2: //│ _1 = Tuple2: -//│ _1 = scope$0$capture +//│ _1 = k //│ _2 = class hkmc2.semantics.VarSymbol //│ _2 = Scope: //│ parent = S of Scope: @@ -392,35 +414,85 @@ g()(1) //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, $block$res -> block$res12, member:h -> h, member:f -> f5, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, $block$res -> block$res5, member:f -> f9, member:g -> g9, member:f -> f10, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, $block$res -> block$res9, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, class:f$capture -> f$capture, member:h -> h2, member:f -> f11, member:f$capture -> f$capture1, member:g -> g10, $block$res -> block$res2, member:h$ -> h$, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$5, member:g -> g, member:f -> f, $block$res -> block$res6, member:g -> g6, $block$res -> block$res14, member:f -> f6, member:h$ -> h$2, member:g$ -> g$3, member:Predef -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res10, member:g$ -> g$6, $block$res -> block$res3, member:g -> g7, member:f -> f7, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, $block$res -> block$res11, gRet -> gRet, g2 -> g21, member:g$ -> g$7, hFun -> hFun, iFun -> iFun, a -> a, member:g -> g8, b -> b, member:h -> h1, member:f -> f8, c -> c, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) //│ curThis = N -//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:scope$0$capture -> scope$0$capture, member:scope$0$capture -> scope$0$capture1, member:f$ -> f$1) +//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) +//│ curThis = S of N +//│ bindings = HashMap(x -> x, scope0$cap -> scope0$cap) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' +//│ ║ l.369: let k = 4 +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.369: let k = 4 +//│ ╙── ^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = scope0$cap +//│ _2 = class hkmc2.semantics.VarSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ curThis = N +//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) //│ curThis = S of N //│ bindings = HashMap(x -> x) //│ curThis = N //│ bindings = HashMap(k -> k) -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope$0$capture' +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' //│ JS (unsanitized): -//│ let h3, f12, g11, tmp5, scope$0$capture1, f$1, h$3; -//│ (class scope$0$capture { +//│ let h3, f12, g11, tmp5, Capture$scope05, f$1, h$3, h$4; +//│ h$3 = function h$(scope0$cap, x) { +//│ return () => { +//│ return h3(scope0$cap, x) +//│ } +//│ }; +//│ h$3 = function h$(scope0$cap, x) { +//│ return () => { +//│ return h3(scope0$cap, x) +//│ } +//│ }; +//│ h3 = function h(scope0$cap, x) { +//│ k = 5; +//│ x = 4; +//│ return x + scope0$cap.y$0 +//│ }; +//│ f$1 = function f$() { +//│ return (x) => { +//│ return f12(x) +//│ } +//│ }; +//│ (class Capture$scope04 { //│ static { -//│ scope$0$capture1 = this +//│ Capture$scope05 = this //│ } -//│ constructor(y$capture$0) { -//│ this.y$capture$0 = y$capture$0; +//│ constructor(y$0) { +//│ this.y$0 = y$0; //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "scope$0$capture"]; +//│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); -//│ h$3 = function h$(scope$0$captureAAAA, x) { +//│ h$4 = function h$(scope0$cap, x) { //│ return () => { -//│ return h3(scope$0$captureAAAA, x) +//│ return h3(scope0$cap, x) //│ } //│ }; -//│ h3 = function h(scope$0$captureAAAA, x) { +//│ h$4 = function h$(scope0$cap, x) { +//│ return () => { +//│ return h3(scope0$cap, x) +//│ } +//│ }; +//│ h3 = function h(scope0$cap, x) { //│ k = 5; //│ x = 4; -//│ return x + scope$0$captureAAAA.y$capture$0 +//│ return x + scope0$cap.y$0 //│ }; //│ f$1 = function f$() { //│ return (x) => { @@ -429,31 +501,35 @@ g()(1) //│ }; //│ f12 = function f(x) { //│ let k; +//│ h3 = function h(scope0$cap, x1) { +//│ k = 5; +//│ x1 = 4; +//│ return x1 + scope0$cap.y$0 +//│ }; //│ k = 4; -//│ scope$0$capture.y$capture$0 = 2; +//│ scope0$cap.y$0 = 2; //│ return x //│ }; //│ g11 = function g() { -//│ let y, scope$0$capture2; -//│ scope$0$capture2 = new scope$0$capture1(undefined); -//│ scope$0$capture2.y$capture$0 = 0; +//│ let y, scope0$cap; +//│ scope0$cap = new Capture$scope05(undefined); +//│ f12 = function f(x) { +//│ let k; +//│ h3 = function h(scope0$cap1, x1) { +//│ k = 5; +//│ x1 = 4; +//│ return x1 + scope0$cap1.y$0 +//│ }; +//│ k = 4; +//│ scope0$cap.y$0 = 2; +//│ return x +//│ }; +//│ scope0$cap.y$0 = 0; //│ return f12 //│ }; //│ tmp5 = g11(); //│ runtime.safeCall(tmp5(1)) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: scope$0$capture is not defined -//│ at f (REPL61:1:1136) -//│ at REPL61:1:1456 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ = 1 :expect 2 fun f() = @@ -485,52 +561,37 @@ f(0, 0, 0)() //│ FAILURE LOCATION: lookup_! (Scope.scala:114) //│ FAILURE INFO: Tuple2: //│ _1 = Tuple2: -//│ _1 = f$capture +//│ _1 = f$cap //│ _2 = class hkmc2.semantics.VarSymbol //│ _2 = Scope: //│ parent = S of Scope: //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, $block$res -> block$res7, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N //│ bindings = HashMap() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$capture' +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$cap' //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) //│ FAILURE INFO: Tuple2: //│ _1 = Tuple2: -//│ _1 = f$capture +//│ _1 = f$cap //│ _2 = class hkmc2.semantics.VarSymbol //│ _2 = Scope: //│ parent = S of Scope: //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, $block$res -> block$res7, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N //│ bindings = HashMap() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$capture' -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: f$capture is not defined -//│ at h (REPL68:1:1067) -//│ at g (REPL68:1:874) -//│ at REPL68:1:274 -//│ at REPL68:1:1973 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '6', got: 'undefined' +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$cap' +//│ = 6 fun f(x, cond) = set x = 1 @@ -553,24 +614,19 @@ f() //│ FAILURE LOCATION: lookup_! (Scope.scala:114) //│ FAILURE INFO: Tuple2: //│ _1 = Tuple2: -//│ _1 = x +//│ _1 = scope0$cap //│ _2 = class hkmc2.semantics.VarSymbol //│ _2 = Scope: //│ parent = S of Scope: //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:scope$0$capture -> scope$0$capture1, $block$res -> block$res4, member:f$ -> f$1, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:h$ -> h$3, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, member:g -> g12, $Shape -> Shape, $tmp -> tmp1, member:f -> f13, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, ret -> ret1, $block$res -> block$res16, $tmp -> tmp6, class:scope$0$capture -> scope$0$capture2, member:scope$0$capture -> scope$0$capture3, $block$res -> block$res5, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:g$ -> g$2, member:i -> i, member:f -> f14, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:f$capture -> f$capture, member:f$capture -> f$capture1, $block$res -> block$res17, member:h$ -> h$, $tmp -> tmp7, class:f$capture -> f$capture2, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:f$capture -> f$capture3, member:i$ -> i$, member:h$ -> h$4, member:g -> g4, member:f -> f4, member:g$ -> g$11, $block$res -> block$res10, member:g$ -> g$6, member:f -> f15, member:g -> g7, member:f -> f7, $block$res -> block$res18, member:lambda -> lambda, member:lambda -> lambda1, member:lambda$ -> lambda$, $block$res -> block$res11, member:lambda$ -> lambda$1, member:g$ -> g$7, member:f -> f16, member:g -> g8, member:g -> g14, member:h -> h1, member:f -> f17, member:f -> f8, $block$res -> block$res7, $block$res -> block$res19, member:f$ -> f$2, member:g$ -> g$12, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, $block$res -> block$res13, member:bar$ -> bar$, member:f$ -> f$, member:bar -> bar1, member:g$ -> g$9, member:foo -> foo1, member:h -> h2, member:f -> f11, member:g -> g10, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res14, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g -> g11, member:Predef -> Predef, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, $block$res -> block$res15, $tmp -> tmp5, c -> c, class:scope$0$capture -> scope$0$capture, d -> d) +//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, member:f -> f15, $block$res -> block$res1, $block$res -> block$res18, member:bar$ -> bar$, member:lambda -> lambda, member:lambda -> lambda1, member:bar -> bar1, member:foo -> foo1, member:lambda$ -> lambda$, $block$res -> block$res12, member:lambda$ -> lambda$1, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:f -> f16, member:g -> g14, member:bar$ -> bar$1, member:g$ -> g$8, member:f -> f17, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, $block$res -> block$res19, class:Capture$scope0 -> Capture$scope08, member:f -> f9, member:g -> g9, member:Capture$scope0 -> Capture$scope09, member:f -> f10, $block$res -> block$res3, member:f$ -> f$2, member:g$ -> g$12, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N //│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' -//│ ║ l.546: let x = 1 -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.546: let x = 1 -//│ ╙── ^ +//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' //│ = 1 let n = 0 @@ -592,11 +648,11 @@ fun f(es, vs) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1261) -//│ at: hkmc2.Lifter.$anonfun$57(Lifter.scala:1296) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1242) +//│ at: hkmc2.Lifter.$anonfun$50(Lifter.scala:1273) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1296) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1320) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1273) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1293) //│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:923) //│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:930) //│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:629) @@ -625,11 +681,19 @@ fun f(x) = //│ return g17(y) //│ } //│ }; +//│ g$15 = function g$(y) { +//│ return () => { +//│ return g17(y) +//│ } +//│ }; //│ g17 = function g(y) { //│ return y //│ }; //│ f20 = function f(x) { //│ let y, scrut, g$here; +//│ g17 = function g(y1) { +//│ return y1 +//│ }; //│ scrut = x < 0; //│ if (scrut === true) { //│ y = 1; @@ -648,22 +712,41 @@ fun f(x) = set x += 1 [a, g] //│ JS (unsanitized): -//│ let g18, f21, g$16; -//│ g$16 = function g$(x) { +//│ let g18, f21, Capture$f5, g$16; +//│ g$16 = function g$(f$cap) { //│ return () => { -//│ return g18(x) +//│ return g18(f$cap) //│ } //│ }; -//│ g18 = function g(x) { -//│ return x +//│ g$16 = function g$(f$cap) { +//│ return () => { +//│ return g18(f$cap) +//│ } //│ }; +//│ g18 = function g(f$cap) { +//│ return f$cap.x$0 +//│ }; +//│ (class Capture$f4 { +//│ static { +//│ Capture$f5 = this +//│ } +//│ constructor(x$0) { +//│ this.x$0 = x$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$f"]; +//│ }); //│ f21 = function f(x) { -//│ let a1, tmp8, g$here; -//│ g$here = g$16(x); +//│ let a1, tmp8, f$cap, g$here; +//│ f$cap = new Capture$f5(undefined); +//│ g18 = function g(f$cap1) { +//│ return f$cap1.x$0 +//│ }; +//│ g$here = g$16(f$cap); //│ a1 = g$here; -//│ tmp8 = x + 1; -//│ x = tmp8; -//│ g$here = g$16(x); +//│ tmp8 = f$cap.x$0 + 1; +//│ f$cap.x$0 = tmp8; +//│ g$here = g$16(f$cap); //│ return globalThis.Object.freeze([ a1, g$here ]) //│ }; From 7461a0c7d9cabaff53d94b4dc879d3dc891edffc Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 16 Jan 2026 00:05:42 +0800 Subject: [PATCH 05/18] fix duplicate defns --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 8 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 59 +++++ .../src/test/mlscript/lifter/FunInFun.mls | 240 ++++++------------ 3 files changed, 147 insertions(+), 160 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 53f68ef102..d09ff03d84 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -936,6 +936,14 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case b: Block => b case _ => die l.copy(body = blk) + case Define(defn, rest) => + val dsym = defn match + case f: FunDefn => f.dSym + case v: ValDefn => v.tsym + case c: ClsLikeDefn => c.isym + ctx.liftedScopes.get(dsym) match + case Some(_) => applySubBlock(rest) + case None => super.applyBlock(b) case _ => super.applyBlock(b) override def applyFunDefn(fun: FunDefn) = applyRewrittenScope(ctx.rewrittenScopes(fun.dSym)) match diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 4169f2b613..a5c2cfb6f7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -49,3 +49,62 @@ fun f() = f() //│ Elab: { fun member:f‹701›() = { let x‹703›; x‹703› = member:g‹700›#666; let y‹704›; y‹704› = 2; fun member:g‹700›() = y‹704›#666; x‹703›#666() }; member:f‹701›#666() } //│ = 2 + +:lift +:sjs +fun bruh = + fun f(x) = + fun g() = + let res = x + h() + res + fun h() = f(0) + g() +//│ Elab: { fun member:bruh‹717› = { fun member:f‹716›(x‹719›) = { fun member:g‹714›() = { let res‹722›; res‹722› = builtin:+‹48›#1(x‹719›#666, member:h‹715›#666()); res‹722›#666 }; fun member:h‹715›() = member:f‹716›#666(0); member:g‹714›#666() }; () }; } +//│ JS (unsanitized): +//│ let g, h, f2, bruh, f$, h$, g$, g_h_f; +//│ g_h_f = function g_h_f(id, param0) { +//│ loopLabel: while (true) { +//│ switch (id) { +//│ case 0: +//│ let res, tmp; +//│ tmp = h(); +//│ res = param0 + tmp; +//│ return res; +//│ break; +//│ case 1: +//│ param0 = 0; +//│ id = 2; +//│ continue loopLabel; +//│ break; +//│ case 2: +//│ id = 0; +//│ continue loopLabel; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ g$ = function g$(x) { +//│ return () => { +//│ return g(x) +//│ } +//│ }; +//│ h$ = function h$() { +//│ return () => { +//│ return h() +//│ } +//│ }; +//│ g = function g(x) { +//│ return g_h_f(0, x) +//│ }; +//│ h = function h() { +//│ return g_h_f(1, undefined) +//│ }; +//│ f$ = function f$() { +//│ return (x) => { +//│ return f2(x) +//│ } +//│ }; +//│ f2 = function f(x) { return g_h_f(2, x) }; +//│ bruh = function bruh() { return runtime.Unit }; + diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 3a80147933..1df91fb01e 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -63,28 +63,18 @@ fun f(used1, unused1) = f(1, 2) //│ JS (unsanitized): //│ let g3, f3, g$3; -//│ g$3 = function g$(used2, used1) { +//│ g$3 = function g$(used1, used2) { //│ return (g_arg) => { -//│ return g3(used2, used1, g_arg) +//│ return g3(used1, used2, g_arg) //│ } //│ }; -//│ g$3 = function g$(used2, used1) { -//│ return (g_arg) => { -//│ return g3(used2, used1, g_arg) -//│ } -//│ }; -//│ g3 = function g(used2, used1, g_arg) { +//│ g3 = function g(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; //│ return used1 + used2 //│ }; //│ f3 = function f(used1, unused1) { //│ let used2, unused2, foo2, tmp1, g$here; -//│ g3 = function g(used21, used11, g_arg) { -//│ let used3; -//│ used3 = 2; -//│ return used11 + used21 -//│ }; //│ used2 = unused1; //│ unused2 = 2; //│ g$here = g$3(used1, used2); @@ -103,17 +93,12 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let g4, f4, g$4; -//│ g$4 = function g$(a3, a6, a2, a4, a5, a1) { +//│ g$4 = function g$(a3, a4, a5, a2, a6, a1) { //│ return () => { -//│ return g4(a3, a6, a2, a4, a5, a1) +//│ return g4(a3, a4, a5, a2, a6, a1) //│ } //│ }; -//│ g$4 = function g$(a3, a6, a2, a4, a5, a1) { -//│ return () => { -//│ return g4(a3, a6, a2, a4, a5, a1) -//│ } -//│ }; -//│ g4 = function g(a3, a6, a2, a4, a5, a1) { +//│ g4 = function g(a3, a4, a5, a2, a6, a1) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = a1 + a2; //│ tmp2 = tmp1 + a3; @@ -123,14 +108,6 @@ f(1,2,3,4,5,6) //│ }; //│ f4 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp1; -//│ g4 = function g(a31, a61, a21, a41, a51, a11) { -//│ let tmp2, tmp3, tmp4, tmp5; -//│ tmp2 = a11 + a21; -//│ tmp3 = tmp2 + a31; -//│ tmp4 = tmp3 + a41; -//│ tmp5 = tmp4 + a51; -//│ return tmp5 + a61 -//│ }; //│ tmp1 = g4(a1, a2, a3, a4, a5, a6); //│ return tmp1 //│ }; @@ -154,10 +131,10 @@ x + y //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1244) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1304) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1252) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1312) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1303) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1311) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -178,10 +155,10 @@ fun f(used1) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1244) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1304) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1252) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1312) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1303) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1311) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -209,26 +186,26 @@ a.toString() + b + c + d //│ _2 = Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, ret -> ret, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, $block$res -> block$res7, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, member:g$ -> g$4, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res, $block$res -> block$res5, member:bar -> bar, member:foo -> foo, member:g$ -> g$2, $block$res -> block$res1, member:bar$ -> bar$, member:g -> g3, member:bar -> bar1, member:f -> f3, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, member:Predef -> Predef, $block$res -> block$res6, member:g$ -> g$3, $block$res -> block$res3, member:g$ -> g$, member:g -> g4, member:f -> f4, member:g -> g1, member:f -> f1) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res6, member:g$ -> g$3, member:Predef‹726› -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//│ ║ l.192: let ret = f(1) +//│ ║ l.169: let ret = f(1) //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.169: fun f(used1) = +//│ ║ l.146: fun f(used1) = //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.170: fun g1(used2) = +//│ ║ l.147: fun g1(used2) = //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.171: fun h() = +//│ ║ l.148: fun h() = //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.172: set used1 = 10 +//│ ║ l.149: set used1 = 10 //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.173: ... (more lines omitted) ... +//│ ║ l.150: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 //│ at Runtime.checkArgs (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:615:41) -//│ at f (REPL16:1:819) +//│ at f (REPL16:1:535) //│ at REPL31:1:180 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) @@ -263,16 +240,6 @@ fun f(unused, immutable, mutated) = f(1, 2, 1000) //│ JS (unsanitized): //│ let g5, h, f5, Capture$f1, h$, g$5; -//│ h$ = function h$(f$cap) { -//│ return () => { -//│ return h(f$cap) -//│ } -//│ }; -//│ g$5 = function g$(f$cap, immutable) { -//│ return () => { -//│ return g5(f$cap, immutable) -//│ } -//│ }; //│ g$5 = function g$(f$cap, immutable) { //│ return () => { //│ return g5(f$cap, immutable) @@ -303,13 +270,6 @@ f(1, 2, 1000) //│ f5 = function f(unused, immutable, mutated) { //│ let a1, tmp5, tmp6, f$cap; //│ f$cap = new Capture$f1(undefined); -//│ g5 = function g(f$cap1, immutable1) { -//│ f$cap1.mutated$0 = 2; -//│ return immutable1 + f$cap1.mutated$0 -//│ }; -//│ h = function h(f$cap1) { -//│ return f$cap1.mutated$0 -//│ }; //│ a1 = g5(f$cap, immutable); //│ tmp5 = h(f$cap); //│ tmp6 = a1 + tmp5; @@ -389,43 +349,18 @@ g()(1) //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) -//│ curThis = N -//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) -//│ curThis = S of N -//│ bindings = HashMap(x -> x, scope0$cap -> scope0$cap) -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' -//│ ║ l.369: let k = 4 -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.369: let k = 4 -//│ ╙── ^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = k -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, $block$res -> block$res5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ curThis = N -//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) +//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1) //│ curThis = S of N //│ bindings = HashMap(x -> x, scope0$cap -> scope0$cap) //│ curThis = N //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' -//│ ║ l.369: let k = 4 +//│ ║ l.329: let k = 4 //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.369: let k = 4 +//│ ║ l.329: let k = 4 //│ ╙── ^ //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) @@ -439,36 +374,16 @@ g()(1) //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, $block$res -> block$res6, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, member:g -> g6, member:f -> f6, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:g$ -> g$7, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, $block$res -> block$res5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ curThis = N -//│ bindings = HashMap(member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:h$ -> h$4, member:f$ -> f$1, member:h$ -> h$3, member:h -> h3, member:f -> f12) +//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1) //│ curThis = S of N //│ bindings = HashMap(x -> x) //│ curThis = N //│ bindings = HashMap(k -> k) //│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' //│ JS (unsanitized): -//│ let h3, f12, g11, tmp5, Capture$scope05, f$1, h$3, h$4; -//│ h$3 = function h$(scope0$cap, x) { -//│ return () => { -//│ return h3(scope0$cap, x) -//│ } -//│ }; -//│ h$3 = function h$(scope0$cap, x) { -//│ return () => { -//│ return h3(scope0$cap, x) -//│ } -//│ }; -//│ h3 = function h(scope0$cap, x) { -//│ k = 5; -//│ x = 4; -//│ return x + scope0$cap.y$0 -//│ }; -//│ f$1 = function f$() { -//│ return (x) => { -//│ return f12(x) -//│ } -//│ }; +//│ let h3, f12, g11, tmp5, Capture$scope05, f$1, h$3; //│ (class Capture$scope04 { //│ static { //│ Capture$scope05 = this @@ -479,12 +394,7 @@ g()(1) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); -//│ h$4 = function h$(scope0$cap, x) { -//│ return () => { -//│ return h3(scope0$cap, x) -//│ } -//│ }; -//│ h$4 = function h$(scope0$cap, x) { +//│ h$3 = function h$(scope0$cap, x) { //│ return () => { //│ return h3(scope0$cap, x) //│ } @@ -501,11 +411,6 @@ g()(1) //│ }; //│ f12 = function f(x) { //│ let k; -//│ h3 = function h(scope0$cap, x1) { -//│ k = 5; -//│ x1 = 4; -//│ return x1 + scope0$cap.y$0 -//│ }; //│ k = 4; //│ scope0$cap.y$0 = 2; //│ return x @@ -513,23 +418,24 @@ g()(1) //│ g11 = function g() { //│ let y, scope0$cap; //│ scope0$cap = new Capture$scope05(undefined); -//│ f12 = function f(x) { -//│ let k; -//│ h3 = function h(scope0$cap1, x1) { -//│ k = 5; -//│ x1 = 4; -//│ return x1 + scope0$cap1.y$0 -//│ }; -//│ k = 4; -//│ scope0$cap.y$0 = 2; -//│ return x -//│ }; //│ scope0$cap.y$0 = 0; //│ return f12 //│ }; //│ tmp5 = g11(); //│ runtime.safeCall(tmp5(1)) -//│ = 1 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: scope0$cap is not defined +//│ at f (REPL61:1:1065) +//│ at REPL61:1:1345 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) :expect 2 fun f() = @@ -568,7 +474,7 @@ f(0, 0, 0)() //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N @@ -585,13 +491,28 @@ f(0, 0, 0)() //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res12, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:bar$ -> bar$1, member:g$ -> g$8, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, member:f -> f9, member:g -> g9, member:f -> f10, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N //│ bindings = HashMap() //│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$cap' -//│ = 6 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: f$cap is not defined +//│ at h (REPL68:1:979) +//│ at g (REPL68:1:834) +//│ at REPL68:1:266 +//│ at REPL68:1:1774 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '6', got: 'undefined' fun f(x, cond) = set x = 1 @@ -621,13 +542,28 @@ f() //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $block$res -> block$res14, $Shape -> Shape, ret -> ret, member:h$ -> h$2, member:g$ -> g$1, gRet -> gRet, member:h -> h3, g2 -> g21, member:f -> f12, hFun -> hFun, member:g -> g11, iFun -> iFun, a -> a, b -> b, member:g -> g2, member:f -> f2, c -> c, d -> d, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $selRes -> selRes2, $discarded -> discarded2, $selRes -> selRes3, $discarded -> discarded3, $tmp -> tmp1, $tmp -> tmp2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g -> g5, member:h -> h, member:f -> f5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res15, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1, member:h$ -> h$3, member:h$ -> h$4, $block$res -> block$res9, member:g -> g12, $block$res -> block$res6, member:f -> f13, class:Capture$f -> Capture$f, member:g$ -> g$3, member:Capture$f -> Capture$f1, ret -> ret1, member:h$ -> h$, member:g -> g4, member:f -> f4, member:g$ -> g$5, $block$res -> block$res16, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g6, member:Capture$scope0 -> Capture$scope07, member:f -> f6, member:g$ -> g$10, member:g -> g13, member:h -> h4, member:i -> i, member:f -> f14, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res17, $block$res -> block$res7, $tmp -> tmp7, class:Capture$f -> Capture$f2, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, member:g$ -> g$4, member:Capture$f -> Capture$f3, member:g$ -> g$7, member:i$ -> i$, member:h$ -> h$5, member:g -> g8, member:h -> h1, $block$res -> block$res, member:f -> f8, member:g$ -> g$11, member:bar -> bar, member:foo -> foo, member:f -> f15, $block$res -> block$res1, $block$res -> block$res18, member:bar$ -> bar$, member:lambda -> lambda, member:lambda -> lambda1, member:bar -> bar1, member:foo -> foo1, member:lambda$ -> lambda$, $block$res -> block$res12, member:lambda$ -> lambda$1, $block$res -> block$res2, member:h$ -> h$1, $tmp -> tmp, member:f -> f16, member:g -> g14, member:bar$ -> bar$1, member:g$ -> g$8, member:f -> f17, member:g -> g, member:f -> f, member:Predef -> Predef, member:g_h -> g_h, $block$res -> block$res19, class:Capture$scope0 -> Capture$scope08, member:f -> f9, member:g -> g9, member:Capture$scope0 -> Capture$scope09, member:f -> f10, $block$res -> block$res3, member:f$ -> f$2, member:g$ -> g$12, member:g$ -> g$, member:g -> g1, member:f -> f1, $block$res -> block$res13, member:f$ -> f$, member:g$ -> g$9, member:h -> h2, member:f -> f11, member:g -> g10) +//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, member:f -> f15, $block$res -> block$res18, member:lambda -> lambda, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:lambda -> lambda1, member:lambda$ -> lambda$, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:lambda$ -> lambda$1, member:g$ -> g$7, member:f -> f16, member:g -> g8, member:g -> g14, member:h -> h1, member:f -> f17, member:f -> f8, member:g$ -> g$4, $block$res -> block$res19, class:Capture$scope0 -> Capture$scope08, member:Capture$scope0 -> Capture$scope09, member:f$ -> f$2, $block$res -> block$res12, member:g$ -> g$12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) //│ curThis = S of N //│ bindings = HashMap($args -> args) //│ curThis = N //│ bindings = HashMap() //│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' -//│ = 1 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: scope0$cap is not defined +//│ at f (REPL74:1:862) +//│ at f (REPL74:1:1106) +//│ at REPL74:1:1173 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' let n = 0 //│ n = 0 @@ -648,11 +584,11 @@ fun f(es, vs) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1242) -//│ at: hkmc2.Lifter.$anonfun$50(Lifter.scala:1273) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1250) +//│ at: hkmc2.Lifter.$anonfun$50(Lifter.scala:1281) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1273) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1293) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1281) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1301) //│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:923) //│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:930) //│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:629) @@ -681,19 +617,11 @@ fun f(x) = //│ return g17(y) //│ } //│ }; -//│ g$15 = function g$(y) { -//│ return () => { -//│ return g17(y) -//│ } -//│ }; //│ g17 = function g(y) { //│ return y //│ }; //│ f20 = function f(x) { //│ let y, scrut, g$here; -//│ g17 = function g(y1) { -//│ return y1 -//│ }; //│ scrut = x < 0; //│ if (scrut === true) { //│ y = 1; @@ -718,11 +646,6 @@ fun f(x) = //│ return g18(f$cap) //│ } //│ }; -//│ g$16 = function g$(f$cap) { -//│ return () => { -//│ return g18(f$cap) -//│ } -//│ }; //│ g18 = function g(f$cap) { //│ return f$cap.x$0 //│ }; @@ -739,9 +662,6 @@ fun f(x) = //│ f21 = function f(x) { //│ let a1, tmp8, f$cap, g$here; //│ f$cap = new Capture$f5(undefined); -//│ g18 = function g(f$cap1) { -//│ return f$cap1.x$0 -//│ }; //│ g$here = g$16(f$cap); //│ a1 = g$here; //│ tmp8 = f$cap.x$0 + 1; From 503e48766da0046fef9b2efd754f65fc45ff5e5a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 16 Jan 2026 00:27:12 +0800 Subject: [PATCH 06/18] fix access ananlyzer --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 12 +-- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 1 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 88 +------------------ 3 files changed, 8 insertions(+), 93 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index d09ff03d84..0248145f4e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -1055,8 +1055,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): /** Maps scopes to the path to the path representing their captures within this object. */ protected val capSymsMap: Map[ScopedInfo, Path] - protected lazy val capturesOrder: List[ScopedInfo] - protected lazy val passedSymsOrder: List[Local] + protected lazy val capturesOrdered: List[ScopedInfo] + protected lazy val passedSymsOrdered: List[Local] override lazy val capturePaths = if thisCapturedLocals.isEmpty then capSymsMap @@ -1085,8 +1085,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): fromParents ++ pathsFromThisObj def formatArgs(captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): List[Arg] = - val captureArgs = capturesOrder.map(c => captures(c).asArg) - val localArgs = passedSymsOrder.map(l => locals(l).asArg) + val captureArgs = capturesOrdered.map(c => captures(c).asArg) + val localArgs = passedSymsOrdered.map(l => locals(l).asArg) captureArgs ::: localArgs /** @@ -1154,8 +1154,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): i -> VarSymbol(Tree.Ident(nme + "$cap")) .toMap - override lazy val capturesOrder: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) - override lazy val passedSymsOrder: List[Local] = passedSyms.toList.sortBy(_.uid) + override lazy val capturesOrdered: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) + override lazy val passedSymsOrdered: List[Local] = passedSyms.toList.sortBy(_.uid) override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index ba9bc5df18..f797ec215a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -55,6 +55,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case s: Scoped => accessed.refdDefns.add(scopeData.getUID(s)) case Assign(lhs, rhs, rest) => + accessed.accessed.add(lhs) accessed.mutated.add(lhs) applyResult(rhs) applyBlock(rest) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index a5c2cfb6f7..4cbe4d6f5e 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -9,7 +9,7 @@ :todo :lift -:ssjs +:rewriteWhile :noSanityCheck fun f() = let i = 0 @@ -18,93 +18,7 @@ fun f() = i f() //│ Elab: { fun member:f‹686›() = { let i‹688›; i‹688› = 0; while { let scrut = builtin:<‹33›#0(i‹688›#666, 10); $scrut‹690›#0 is true then i‹688›#666 := builtin:+‹48›#0(i‹688›#666, 1); else () }; i‹688›#666 }; member:f‹686›#666() } -//│ JS: -//│ let f; -//│ f = function f() { -//│ let i, tmp; -//│ i = 0; -//│ lbl: while (true) { -//│ let scrut, tmp1; -//│ scrut = i < 10; -//│ if (scrut === true) { -//│ tmp1 = i + 1; -//│ i = tmp1; -//│ tmp = runtime.Unit; -//│ continue lbl -//│ } else { tmp = runtime.Unit; } -//│ break; -//│ } -//│ return i -//│ }; -//│ block$res1 = f(); -//│ undefined //│ = 10 -:expect 2 -fun f() = - let x = g - let y = 2 - fun g() = y - x() -f() -//│ Elab: { fun member:f‹701›() = { let x‹703›; x‹703› = member:g‹700›#666; let y‹704›; y‹704› = 2; fun member:g‹700›() = y‹704›#666; x‹703›#666() }; member:f‹701›#666() } -//│ = 2 -:lift -:sjs -fun bruh = - fun f(x) = - fun g() = - let res = x + h() - res - fun h() = f(0) - g() -//│ Elab: { fun member:bruh‹717› = { fun member:f‹716›(x‹719›) = { fun member:g‹714›() = { let res‹722›; res‹722› = builtin:+‹48›#1(x‹719›#666, member:h‹715›#666()); res‹722›#666 }; fun member:h‹715›() = member:f‹716›#666(0); member:g‹714›#666() }; () }; } -//│ JS (unsanitized): -//│ let g, h, f2, bruh, f$, h$, g$, g_h_f; -//│ g_h_f = function g_h_f(id, param0) { -//│ loopLabel: while (true) { -//│ switch (id) { -//│ case 0: -//│ let res, tmp; -//│ tmp = h(); -//│ res = param0 + tmp; -//│ return res; -//│ break; -//│ case 1: -//│ param0 = 0; -//│ id = 2; -//│ continue loopLabel; -//│ break; -//│ case 2: -//│ id = 0; -//│ continue loopLabel; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ g$ = function g$(x) { -//│ return () => { -//│ return g(x) -//│ } -//│ }; -//│ h$ = function h$() { -//│ return () => { -//│ return h() -//│ } -//│ }; -//│ g = function g(x) { -//│ return g_h_f(0, x) -//│ }; -//│ h = function h() { -//│ return g_h_f(1, undefined) -//│ }; -//│ f$ = function f$() { -//│ return (x) => { -//│ return f2(x) -//│ } -//│ }; -//│ f2 = function f(x) { return g_h_f(2, x) }; -//│ bruh = function bruh() { return runtime.Unit }; From 12a4be74674d183dd4bca99bbb7a560a684f2721 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 16 Jan 2026 16:29:34 +0800 Subject: [PATCH 07/18] lift things inside top level classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 89 +++++++++++++------ .../main/scala/hkmc2/codegen/ScopeData.scala | 6 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 7 -- .../scala/hkmc2/codegen/js/JSBuilder.scala | 2 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 87 +++++++++++++++--- 5 files changed, 140 insertions(+), 51 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 0248145f4e..bb80f18d38 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -510,12 +510,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case _ => super.applyResult(r)(k) // extract the call - override def applyPath(p: Path)(k: Path => Block): Block = - p match + override def applyPath(p: Path)(k: Path => Block): Block = p match case r @ RefOfBms(l, S(d)) => ctx.liftedScopes.get(d) match case S(f: LiftedFunc) => if f.isTrivial then k(r) - else + else val newSym = closureMap.get(l) match case None => val newSym = TempSymbol(N, l.nme + "$here") @@ -530,7 +529,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case Some(value) => syms.addOne(FunSyms(l, d) -> value) value - k(Value.Ref(newSym, S(d))) + k(Value.Ref(newSym, N)) // Other naked references to BlockMemberSymbols. case N => ctx.symbolsMap.get(d) match @@ -579,27 +578,27 @@ class Lifter(topLevelBlk: Block)(using State, Raise): if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) - then b else Match(scrut2, arms2, dflt2, rst2) + then rewritten else Match(scrut2, arms2, dflt2, rst2) case Label(lbl, false, bod, rst) => val lbl2 = lbl.subst val bod2 = applySubBlockAndReset(bod) val rst2 = applySubBlock(rst) - if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, false, bod2, rst2) + if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then rewritten else Label(lbl2, false, bod2, rst2) case TryBlock(sub, fin, rst) => val sub2 = applySubBlockAndReset(sub) val fin2 = applySubBlockAndReset(fin) val rst2 = applySubBlock(rst) - if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then b else TryBlock(sub2, fin2, rst2) + if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then rewritten else TryBlock(sub2, fin2, rst2) // Assignment to variables case Assign(lhs, rhs, rest) => ctx.symbolsMap.get(lhs) match case Some(path) => applyResult(rhs): rhs2 => path.assign(rhs2, applySubBlock(rest)) - case _ => super.applyBlock(b) + case _ => super.applyBlock(rewritten) // rewrite ValDefns (in ctors) - case Define(d: ValDefn, rest: Block) if d.owner.isDefined => super.applyBlock(b) // TODO + case define @ Define(d: ValDefn, rest: Block) if d.owner.isDefined => super.applyBlock(rewritten) // TODO /* ctx.getIsymPath(d.owner.get) match case Some(value) if !iSymInScope(d.owner.get) => @@ -608,7 +607,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case _ => super.applyBlock(rewritten) */ // rewrite object definitions, assigning to the given symbol in modObjLocals - case Define(d: ClsLikeDefn, rest: Block) => super.applyBlock(b) // TODO + case Define(d: ClsLikeDefn, rest: Block) => super.applyBlock(rewritten) // TODO /* ctx.modObjLocals.get(d.sym) match case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match @@ -836,6 +835,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): println(v) println(")") + /* println("accessesShallow") printMap(usedVars.shallowAccesses) @@ -845,6 +845,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): println("usedVars") printMap(usedVars.reqdCaptures) + */ def isIgnored(d: Defn) = d match case f: FunDefn => ignored.contains(f.dSym) @@ -981,6 +982,24 @@ class Lifter(topLevelBlk: Block)(using State, Raise): protected def rewriteImpl: LifterResult[T] + protected def addCaptureSym(b: Block, captureSym: Local, define: Bool): Block = + if hasCapture then + val undef = Value.Lit(Tree.UnitLit(false)).asArg + val inst = Instantiate( + true, + Value.Ref(captureClass.sym, S(captureClass.isym)), + List.fill(thisCapturedLocals.size)(undef) + ) + val assign = Assign(captureSym, inst, b) + if define then + Scoped( + Set(captureSym), + assign + ) + else assign + else + b + /** * Rewrites the contents of this scoped object to reference the lifted versions of variables. * @@ -1093,23 +1112,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise): * A rewritten scope with a generic VarSymbol capture symbol. */ sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: - lazy val captureSym = VarSymbol(Tree.Ident(this.obj.nme + "$cap")) + lazy val captureSym = VarSymbol(Tree.Ident(obj.nme + "$cap")) override lazy val capturePath = captureSym.asPath - protected def addCaptureSym(b: Block): Block = - if hasCapture then - val undef = Value.Lit(Tree.UnitLit(false)).asArg - val inst = Instantiate( - true, - Value.Ref(captureClass.sym, S(captureClass.isym)), - List.fill(thisCapturedLocals.size)(undef) - ) - Scoped( - Set(captureSym), - Assign(captureSym, inst, b) - ) - else - b + protected def addCaptureSym(b: Block): Block = addCaptureSym(b, captureSym, true) // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) @@ -1144,6 +1150,35 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewritten = rewriter.rewrite(obj.fun.body) val withCapture = addCaptureSym(rewritten) LifterResult(obj.fun.copy(body = withCapture)(obj.fun.forceTailRec), rewriter.extraDefns.toList) + + class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends RewrittenScope[ClsLikeDefn](obj): + + private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) + override lazy val capturePath: Path = captureSym.asPath + + protected def rewriteMethods = + val mtds = node.children + .map: c => + ctx.rewrittenScopes(c.obj.toInfo) + .collect: + case r: RewrittenFunc if r.obj.isMethod => r + val (liftedMtds, extras) = mtds.map(liftNestedScopes).unzip(using l => (l.liftedDefn, l.extraDefns)) + LifterResult(liftedMtds, extras.flatten) + + override def rewriteImpl: LifterResult[ClsLikeDefn] = + val rewriterCtor = new BlockRewriter + val rewriterPreCtor = new BlockRewriter + val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) + val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) + val preCtorWithCap = addCaptureSym(rewrittenPrector, captureSym, false) + val LifterResult(newMtds, extras) = rewriteMethods + val newCls = obj.cls.copy( + ctor = rewrittenCtor, + preCtor = preCtorWithCap, + privateFields = captureSym :: obj.cls.privateFields, + methods = newMtds + ) + LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) class LiftedFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends LiftedScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: private val passedSymsMap_ : Map[Local, VarSymbol] = passedSyms.map: s => @@ -1247,7 +1282,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise): private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") - case o: ScopedObject.Class => ??? + case o: ScopedObject.Class => + if s.isLifted && !s.isTopLevel then ??? + else RewrittenClass(o) case o: ScopedObject.Companion => ??? case o: ScopedObject.ClassCtor => ??? case o: ScopedObject.Func => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index a415e8b723..759e96b244 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -264,10 +264,7 @@ class ScopeData(b: Block)(using State, IgnoredScopes): def getNode(defn: FunDefn): ScopeNode = getNode(defn.dSym) def getUID(blk: Scoped): ScopeUID = if scopedMap.containsKey(blk) then scopedMap.get(blk) - else - println("key not found:") - println(blk) - lastWords("getUID: key not found") + else lastWords("getUID: key not found") def getNode(blk: Scoped): ScopeNode = getNode(getUID(blk)) // From the input block or definition, traverses until a function, class or new scoped block is found and appends them. class ScopeFinder extends BlockTraverserShallow: @@ -275,7 +272,6 @@ class ScopeData(b: Block)(using State, IgnoredScopes): override def applyBlock(b: Block): Unit = b match case s: Scoped => val id = fresh.make - println("fresh: " + id) objs ::= ScopedObject.ScopedBlock(id, s) case l: Label if l.loop => objs ::= ScopedObject.Loop(l.label, l.body) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index f797ec215a..da5c1bfa24 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -209,9 +209,6 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // "ignore" `c` in the sense that it does not need to capture `s`'s scoped object's variables, nor does // it require the current scoped object to create a capture class for its accessed variables. def isIgnored(c: ScopedInfo) = - println("in " + s.obj.toInfo + ":") - println(" " + c.toString + " is ignored: " + s.inSubtree(scopeData.getNode(c).firstLiftedParent.toInfo)) - println(" first lifted parent of " + c + ": " + scopeData.getNode(c).firstLiftedParent.toInfo) s.inSubtree(scopeData.getNode(c).firstLiftedParent.toInfo) // All objects in the same scc must have at least the same accesses as each other @@ -317,8 +314,6 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes rec(l.body)(using isLinear = false) |> merge applyBlock(l.rest) case Assign(lhs, rhs, rest) => - println("assign: " + lhs + " = " + rhs) - println("has readers: " + hasReader) applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs applyBlock(rest) @@ -402,7 +397,6 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(d) val muts = muted.intersect(thisVars) val reads = accessed.intersect(thisVars) -- muts - println("naked ref: " + p) // this is a naked reference, we assume things it mutates always needs a capture for l <- muts do reqCapture += l @@ -410,7 +404,6 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes for l <- reads do if hasMutator.contains(l) then reqCapture += l - println("has reader: " + l) hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala index 694a78a85c..f409ffd27b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -266,6 +266,8 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: => val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) + println(isym) + println(ctorParams) val ctorAuxParams = auxParams.map(ps => ps.params.map(p => p.sym -> scope.allocateName(p.sym))) def mkMethods(mtds: Ls[FunDefn], mtdPrefix: Str)(using Scope): Document = diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 4cbe4d6f5e..fd937162f7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,18 +7,79 @@ :global :d :todo - -:lift -:rewriteWhile :noSanityCheck -fun f() = - let i = 0 - while i < 10 do - set i += 1 - i -f() -//│ Elab: { fun member:f‹686›() = { let i‹688›; i‹688› = 0; while { let scrut = builtin:<‹33›#0(i‹688›#666, 10); $scrut‹690›#0 is true then i‹688›#666 := builtin:+‹48›#0(i‹688›#666, 1); else () }; i‹688›#666 }; member:f‹686›#666() } -//│ = 10 - - +:lift +:ssjs +class A with + let x = 2 + val bruh = () => x + fun f = + fun g() = + set x += 2 + this + g +let a = new A +a.f() +a.f() +a.bruh() +//│ Elab: { Cls A { let term:class:A‹690›.x‹692›; term:class:A‹690›.x‹692› = 2; method val member:bruh‹688› = λ(). term:class:A‹690›.x‹692›#666; method fun member:f‹687› = { fun member:g‹686›() = { term:class:A‹690›.x‹692›#666 := builtin:+‹48›#0(term:class:A‹690›.x‹692›#666, 2); class:A‹690›#0 }; member:g‹686›#666 }; }; let a‹703›; a‹703› = new member:A‹689›#666; a‹703›#666.f(); a‹703›#666.f(); a‹703›#666.bruh() } +//│ JS: +//│ let g, A1, a, lambda, tmp, tmp1, Capture$A1, lambda$, g$; +//│ g$ = function g$(A$cap, A2) { +//│ return () => { +//│ return g(A$cap, A2) +//│ } +//│ }; +//│ g = function g(A$cap, A2) { +//│ let tmp2; +//│ tmp2 = A$cap.x$0 + 2; +//│ A$cap.x$0 = tmp2; +//│ return A2 +//│ }; +//│ lambda$ = (undefined, function (A$cap) { +//│ return () => { +//│ return lambda(A$cap) +//│ } +//│ }); +//│ lambda = (undefined, function (A$cap) { +//│ return A$cap.x$0 +//│ }); +//│ (class Capture$A { +//│ static { +//│ Capture$A1 = this +//│ } +//│ constructor(x$0) { +//│ this.x$0 = x$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$A"]; +//│ }); +//│ (class A { +//│ static { +//│ A1 = this +//│ } +//│ constructor() { +//│ this.A$cap = new Capture$A1(undefined); +//│ let lambda$here; +//│ this.A$cap.x$0 = 2; +//│ lambda$here = lambda$(this.A$cap); +//│ this.bruh = lambda$here; +//│ } +//│ #A$cap; +//│ #x; +//│ get f() { +//│ let g$here; +//│ g$here = g$(this.A$cap, this); +//│ return g$here; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); +//│ a = globalThis.Object.freeze(new A1()); +//│ tmp = runtime.safeCall(a.f()); +//│ tmp1 = runtime.safeCall(a.f()); +//│ block$res1 = runtime.safeCall(a.bruh()); +//│ undefined +//│ = 6 +//│ a = A { A$cap: Capture$A { x$0: 6 }, bruh: fun } From f7b506fd1da8665fd65bcbb3b036a422c572f89e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 16 Jan 2026 20:38:20 +0800 Subject: [PATCH 08/18] class lifting, fix many issues with analyzer --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 183 +++++++-- .../main/scala/hkmc2/codegen/ScopeData.scala | 2 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 66 +++- .../scala/hkmc2/codegen/js/JSBuilder.scala | 2 - hkmc2/shared/src/test/mlscript/HkScratch.mls | 94 ++--- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 59 +++ .../src/test/mlscript/lifter/ClassInFun.mls | 137 +++---- .../mlscript/lifter/ClassWithCompanion.mls | 108 ++++- .../test/mlscript/lifter/CompanionsInFun.mls | 37 +- .../src/test/mlscript/lifter/DefnsInClass.mls | 68 ++-- .../test/mlscript/lifter/EffectHandlers.mls | 17 + .../src/test/mlscript/lifter/FunInFun.mls | 372 ++++-------------- .../src/test/mlscript/lifter/Imports.mls | 36 +- .../shared/src/test/mlscript/lifter/Loops.mls | 82 ++-- .../src/test/mlscript/lifter/Mutation.mls | 47 ++- .../src/test/mlscript/lifter/PatternInFun.mls | 2 +- 16 files changed, 669 insertions(+), 643 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/HkScratch2.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index bb80f18d38..a49d1f5522 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -497,21 +497,23 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version - case c @ Call(RefOfBms(l, S(d)), args) => ctx.liftedScopes.get(d) match + case c @ Call(RefOfBms(l, S(d)), args) => ctx.rewrittenScopes.get(d) match case None => super.applyResult(r)(k) case Some(value) => value match case f: LiftedFunc => k(f.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap)) - case c @ Instantiate(mut, InstSel(l, S(d)), args) => ??? - // LEGACY CODE: We previously directly created the closure and assigned it to the - // variable here. But, since this closure may be re-used later, this doesn't work - // in general, so we will always create a TempSymbol for it. - // case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => - // createCall(l, ctx) + case ctor: RewrittenClassCtor => ctor.getRewrittenCls match + case cls: LiftedClass => + k(cls.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap)) + case _ => super.applyResult(r)(k) + case _ => super.applyResult(r)(k) + case inst @ Instantiate(mut, InstSel(l, S(d)), args) => ctx.rewrittenScopes.get(d) match + case S(c: LiftedClass) => k(c.rewriteInstantiate(inst, ctx.capturesMap, ctx.symbolsMap)) + case _ => super.applyResult(r)(k) case _ => super.applyResult(r)(k) // extract the call override def applyPath(p: Path)(k: Path => Block): Block = p match - case r @ RefOfBms(l, S(d)) => ctx.liftedScopes.get(d) match + case r @ RefOfBms(l, S(d)) => ctx.rewrittenScopes.get(d) match case S(f: LiftedFunc) => if f.isTrivial then k(r) else @@ -531,6 +533,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise): value k(Value.Ref(newSym, N)) + case S(f: RewrittenFunc) if f.obj.isMethod => + val sel = Select(ctx.symbolsMap(f.obj.fun.owner.get).read, Tree.Ident(l.nme))(S(d)) + k(sel) + + case S(_) => super.applyPath(p)(k) + // Other naked references to BlockMemberSymbols. case N => ctx.symbolsMap.get(d) match case Some(value) => k(value.read) @@ -847,6 +855,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): */ + def isIgnored(d: Defn) = d match case f: FunDefn => ignored.contains(f.dSym) case v: ValDefn => true @@ -868,7 +877,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): * `varsMap` maps the function's locals to the corresponding `VarSymbol` (for the class parameters) in the correct order. */ def createCaptureCls(s: ScopedObject) - : (ClsLikeDefn, List[(Local, TermSymbol)]) = + : (ClsLikeDefn, List[(Symbol, TermSymbol)]) = val nme = "Capture$" + s.nme val clsSym = ClassSymbol( @@ -880,7 +889,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val fresh = FreshInt() - val sortedVars = cap.toArray.sortBy(_.uid).map: sym => + val sortedVars: Array[(ctorSyms: (local: Local, vs: VarSymbol), param: Param, valDefn: ValDefn)] = cap.toArray.sortBy(_.uid).map: sym => val id = fresh.make val nme = sym.nme + "$" + id @@ -905,7 +914,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): S(TermSymbol(syntax.Fun, S(clsSym), clsSym.id)), syntax.Cls, N, - PlainParamList(sortedVars.iterator.map(_._2).toList) :: Nil, None, Nil, Nil, + PlainParamList(sortedVars.iterator.map(_.param).toList) :: Nil, None, Nil, Nil, Nil, End(), sortedVars.iterator.foldLeft[Block](End()): @@ -914,7 +923,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): N, ) - (defn, sortedVars.iterator.map(x => x._1._1 -> x._3.tsym).toList) + (defn, sortedVars.iterator.map(x => (x.ctorSyms.local, x.valDefn.tsym)).toList) class ScopeRewriter(using ctx: LifterCtxNew) extends BlockTransformerShallow(SymbolSubst()): @@ -988,7 +997,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val inst = Instantiate( true, Value.Ref(captureClass.sym, S(captureClass.isym)), - List.fill(thisCapturedLocals.size)(undef) + captureInfo._2.map: + case (sym, _) => sym.asPath.asArg ) val assign = Assign(captureSym, inst, b) if define then @@ -1023,8 +1033,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // Locals introduced by this object that are inside this object's capture val fromCap = thisCapturedLocals .map: s => - val vSym = captureMap(s) - s -> LocalPath.InCapture(capturePath, vSym) + val tSym = captureMap(s) + s -> LocalPath.InCapture(capturePath, tSym) .toMap // BMS refs from ignored defns // Note that we map the DefinitionSymbol to the disambiguated BMS. @@ -1151,19 +1161,27 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val withCapture = addCaptureSym(rewritten) LifterResult(obj.fun.copy(body = withCapture)(obj.fun.forceTailRec), rewriter.extraDefns.toList) + private def rewriteMethods(node: ScopeNode, methods: List[FunDefn])(using ctx: LifterCtxNew) = + val mtds = node.children + .map: c => + ctx.rewrittenScopes(c.obj.toInfo) + .collect: + case r: RewrittenFunc if r.obj.isMethod => r + val (liftedMtds, extras) = mtds.map(liftNestedScopes).unzip(using l => (l.liftedDefn, l.extraDefns)) + LifterResult(liftedMtds, extras.flatten) + + class RewrittenClassCtor(override val obj: ScopedObject.ClassCtor)(using ctx: LifterCtxNew) extends RewrittenScope[Unit](obj): + + override lazy val capturePath: Path = lastWords("tried to create a capture class for a class ctor") + + override protected def rewriteImpl: LifterResult[Unit] = LifterResult((), Nil) // dummy + + def getRewrittenCls = ctx.rewrittenScopes(obj.cls.isym) + class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends RewrittenScope[ClsLikeDefn](obj): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath - - protected def rewriteMethods = - val mtds = node.children - .map: c => - ctx.rewrittenScopes(c.obj.toInfo) - .collect: - case r: RewrittenFunc if r.obj.isMethod => r - val (liftedMtds, extras) = mtds.map(liftNestedScopes).unzip(using l => (l.liftedDefn, l.extraDefns)) - LifterResult(liftedMtds, extras.flatten) override def rewriteImpl: LifterResult[ClsLikeDefn] = val rewriterCtor = new BlockRewriter @@ -1171,7 +1189,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) val preCtorWithCap = addCaptureSym(rewrittenPrector, captureSym, false) - val LifterResult(newMtds, extras) = rewriteMethods + val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( ctor = rewrittenCtor, preCtor = preCtorWithCap, @@ -1196,8 +1214,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap val auxParams: List[Param] = - (capSymsMap_.values.toList.sortBy(_.uid) ::: passedSymsMap_.values.toList.sortBy(_.uid)) - .map(Param.simple(_)) + (capturesOrdered.map(capSymsMap_) ::: passedSymsOrdered.map(passedSymsMap_)) + .map: s => + val decl = Param(FldFlags.empty.copy(isVal = false), s, N, Modulefulness.none) + s.decl = S(decl) + decl // Whether this can be lifted without the need to pass extra parameters. val isTrivial = auxParams.isEmpty @@ -1216,7 +1237,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewriter = new BlockRewriter val newBod = rewriter.rewrite(fun.body) val withCapture = addCaptureSym(newBod) - val newDefn = fun.copy(sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)(fun.forceTailRec) + val newDefn = fun.copy(owner = N, sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)(fun.forceTailRec) LifterResult(newDefn, rewriter.extraDefns.toList) // Definition with the auxiliary parameters merged into the second parameter list. @@ -1247,7 +1268,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val bod = Return(call, false) FunDefn( - fun.owner, + N, auxSym, auxDsym, newPlists, @@ -1278,15 +1299,113 @@ class Lifter(topLevelBlk: Block)(using State, Raise): def rewriteImpl: LifterResult[FunDefn] = val LifterResult(lifted, extra) = mkFlattenedDefn - LifterResult(lifted, mkAuxDefn :: extra) + if isTrivial then LifterResult(lifted, extra) + else LifterResult(lifted, mkAuxDefn :: extra) + class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends LiftedScope[ClsLikeDefn](obj): + + private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) + override lazy val capturePath: Path = captureSym.asPath + + private val passedSymsMap_ : Map[Local, (vs: VarSymbol, ts: TermSymbol)] = passedSyms.map: s => + s -> + ( + VarSymbol(Tree.Ident(s.nme)), + TermSymbol(syntax.MutVal, S(obj.cls.isym), Tree.Ident(s.nme)) + ) + .toMap + private val capSymsMap_ : Map[ScopedInfo, (vs: VarSymbol, ts: TermSymbol)] = reqCaptures.map: i => + val nme = data.getNode(i).obj.nme + "$cap" + i -> + ( + VarSymbol(Tree.Ident(nme)), + TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(nme)) + ) + .toMap + + override lazy val capturesOrdered: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).vs.uid) + override lazy val passedSymsOrdered: List[Local] = passedSyms.toList.sortBy(_.uid) + + override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.ts.asLocalPath).toMap + override protected val capSymsMap = capSymsMap_.view.mapValues(_.ts.asPath).toMap + + val auxParams: List[Param] = + (capturesOrdered.map(x => capSymsMap_(x).vs) ::: passedSymsOrdered.map(x => passedSymsMap_(x).vs)) + .map(Param.simple(_)) + + // Whether this can be lifted without the need to pass extra parameters. + val isTrivial = auxParams.isEmpty + + val cls = obj.cls + + def rewriteInstantiate(inst: Instantiate, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Instantiate = + if isTrivial then inst + else + Instantiate( + inst.mut, + Value.Ref(cls.sym, S(cls.isym)), + formatArgs(captures, locals) ::: inst.args + ) + + def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = + if isTrivial then c + else + Call( + Value.Ref(cls.sym, S(cls.ctorSym.get)), + formatArgs(captures, locals) ::: c.args + )( + isMlsFun = true, + mayRaiseEffects = c.mayRaiseEffects, + explicitTailCall = c.explicitTailCall + ) + + def rewriteImpl: LifterResult[ClsLikeDefn] = + val rewriterCtor = new BlockRewriter + val rewriterPreCtor = new BlockRewriter + val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) + val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) + val preCtorWithCap = addCaptureSym(rewrittenPrector, captureSym, false) + + // Assign passed locals and captures + val preCtorWithPassed = passedSymsOrdered.foldRight(preCtorWithCap): + case (sym, acc) => + val (vs, ts) = passedSymsMap_(sym) + Assign(ts, vs.asPath, acc) + val preCtorWithCaps = capturesOrdered.foldRight(preCtorWithPassed): + case (sym, acc) => + val (vs, ts) = capSymsMap_(sym) + Assign(ts, vs.asPath, acc) + + val (newPlist, newAuxList) = cls.paramsOpt match + case Some(plist) => + ( + S(plist.copy(params = auxParams ::: plist.params)), + cls.auxParams + ) + case None => + ( + N, + PlainParamList(auxParams) :: cls.auxParams + ) + + val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) + val newCls = obj.cls.copy( + owner = N, + ctor = rewrittenCtor, + preCtor = preCtorWithCaps, + privateFields = captureSym :: obj.cls.privateFields, + methods = newMtds, + paramsOpt = newPlist, + auxParams = newAuxList + ) + LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") case o: ScopedObject.Class => - if s.isLifted && !s.isTopLevel then ??? + if s.isLifted && !s.isTopLevel then LiftedClass(o) else RewrittenClass(o) case o: ScopedObject.Companion => ??? - case o: ScopedObject.ClassCtor => ??? + case o: ScopedObject.ClassCtor => RewrittenClassCtor(o) case o: ScopedObject.Func => if s.isLifted && !s.isTopLevel then LiftedFunc(o) else RewrittenFunc(o) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index 759e96b244..2f4b922ac0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -219,7 +219,7 @@ object ScopeData: case _: ScopedObject.Top => List.empty case _ => // All unlifted neighbour nodes ::: parent's reqCaptureObjsImpl - val initial = parent.get.allChildNodes.collect: + val initial = parent.get.children.collect: case c @ ScopeNode(obj = t: ScopedObject.Referencable[?]) if !c.isLifted => t initial ::: parent.get.reqCaptureObjsImpl diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index da5c1bfa24..5fe43f55e4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -18,6 +18,12 @@ import scala.jdk.CollectionConverters.* import java.util.IdentityHashMap import java.util.Collections import scala.collection.mutable.Buffer +import hkmc2.ScopeData.ScopedObject.Top +import hkmc2.ScopeData.ScopedObject.Companion +import hkmc2.ScopeData.ScopedObject.ClassCtor +import hkmc2.ScopeData.ScopedObject.Func +import hkmc2.ScopeData.ScopedObject.Loop +import hkmc2.ScopeData.ScopedObject.ScopedBlock object UsedVarAnalyzer: case class MutAccessInfo( @@ -61,13 +67,24 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes applyBlock(rest) case l: Label if l.loop => accessed.refdDefns.add(l.label) - case d: Define => applySubBlock(d.rest) + case d: Define => d.defn match + case v: ValDefn => + applyDefn(v) + applySubBlock(d.rest) + case _ => applySubBlock(d.rest) + case _ => super.applyBlock(b) override def applyPath(p: Path): Unit = p match case Value.Ref(_: BuiltinSymbol, _) => super.applyPath(p) case RefOfBms(_, SDSym(dSym)) if scopeData.contains(dSym) => - accessed.refdDefns.add(scopeData.getNode(dSym).obj.toInfo) + // Check if it's referencing a class method. + // If so, then it requires reading the class symbol + val node = scopeData.getNode(dSym) + node.obj match + case Func(isMethod = false) => accessed.refdDefns.add(node.obj.toInfo) + case f @ Func(isMethod = true) => accessed.accessed.add(f.fun.owner.get) + case _ => () case Value.Ref(l, _) => accessed.accessed.add(l) case _ => super.applyPath(p) @@ -236,7 +253,14 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes yield sym -> sccAccessInfo(id).withoutLocals(scopeData.getNode(sym).obj.definedLocals) - val (m1, m2) = (go(true), go(false)) + // Remove locals that are not yet defined + def removeUnused(m: Map[ScopedInfo, AccessInfo]) = m.map: + case k -> v => + val node = scopeData.getNode(k) + k -> v.intersectLocals(node.existingVars) + + val (m1, m2) = (removeUnused(go(true)), removeUnused(go(false))) + val subCases = nexts.map(findAccesses) subCases.foldLeft((m1, m2)): case ((acc1, acc2), (new1, new2)) => (combineInfos(acc1, new1), combineInfos(acc2, new2)) @@ -267,7 +291,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val locals = nodes.flatMap(_.obj.definedLocals).toSet - val cap = reqdCaptureLocalsBlk(blk, nexts.toList, locals) + val cap = reqdCaptureLocalsBlk(blk, nexts.toList, s.obj.definedLocals, locals) // Variables mutated by a lifted child of a class methods requires a capture val additional = extraMtds @@ -285,37 +309,44 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case (mp, acc) => mp ++ reqdCaptureLocals(acc) // readers-mutators analysis - private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], thisVars: Set[Local]): Set[Local] = + private def reqdCaptureLocalsBlk(b: Block, nextNodes: List[ScopeNode], startingVars: Set[Local], thisVars: Set[Local]): Set[Local] = val scopeInfos: Map[ScopedInfo, ScopeNode] = nextNodes.map(node => node.obj.toInfo -> node).toMap - case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local]) + case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local], mutated: Set[Local]) - // isLinear denotes whether the control flow is linear, i.e. whether `b` could be executed more than once assuming that - // the input block in `reqdCaptureLocalsBlk` is only executed once. - def go(b: Block, reqCapture_ : Set[Local], hasReader_ : Set[Local], hasMutator_ : Set[Local])(using isLinear: Bool): CaptureInfo = + // linearVars denotes the variables defined inside the scopes up to the nearest loop or the top level block. + // If a loop modifies a non-linear variable and then one of its nested definitions accesses it, we must put put + // that variable in a capture. + def go(b: Block, reqCapture_ : Set[Local], hasReader_ : Set[Local], hasMutator_ : Set[Local], mutated_ : Set[Local])(using linearVars: Set[Local]): CaptureInfo = var reqCapture = reqCapture_ var hasReader = hasReader_ var hasMutator = hasMutator_ + // note: the meaning of `mutated` is a bit strange: it basically means variables which are currently not linear that have been mutated + // if a variable is in this set but is linear, then it's ignored + var mutated = mutated_ inline def merge(c: CaptureInfo) = reqCapture ++= c.reqCapture hasReader ++= c.hasReader hasMutator ++= c.hasMutator + mutated ++= c.mutated - def rec(blk: Block)(using isLinear: Bool) = - go(blk, reqCapture, hasReader, hasMutator) + def rec(blk: Block)(using linearVars: Set[Local]) = + go(blk, reqCapture, hasReader, hasMutator, mutated_) new BlockTraverserShallow: applyBlock(b) override def applyBlock(b: Block): Unit = b match // Note that we traverse directly into scoped blocks without using handleCalledScope - + case s: Scoped => + rec(s.body)(using linearVars = linearVars ++ s.syms) |> merge case l: Label if l.loop => - rec(l.body)(using isLinear = false) |> merge + rec(l.body)(using linearVars = Set.empty) |> merge applyBlock(l.rest) case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs + if !linearVars.contains(lhs) then mutated += lhs applyBlock(rest) case Match(scrut, arms, dflt, rest) => @@ -358,13 +389,16 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // so the "one writer" rule applies. // However, if the control flow is not linear, we are forced to add all the mutated variables for l <- muts do - if hasReader.contains(l) || hasMutator.contains(l) || !isLinear then + if hasReader.contains(l) || hasMutator.contains(l) || !linearVars.contains(l) then reqCapture += l hasReader += l hasMutator += l + mutated += l for l <- reads do if hasMutator.contains(l) then reqCapture += l + if mutated.contains(l) && !linearVars.contains(l) then + reqCapture += l hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily @@ -425,9 +459,9 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes super.applyDefn(defn) case _ => super.applyDefn(defn) - CaptureInfo(reqCapture, hasReader, hasMutator) + CaptureInfo(reqCapture, hasReader, hasMutator, mutated) - val reqCapture = go(b, Set.empty, Set.empty, Set.empty)(using isLinear = true).reqCapture + val reqCapture = go(b, Set.empty, Set.empty, Set.empty, Set.empty)(using linearVars = startingVars).reqCapture reqCapture.intersect(thisVars) val reqdCaptures: Map[ScopedInfo, Set[Local]] = scopeData.root.children.foldLeft(Map.empty): diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala index f409ffd27b..694a78a85c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -266,8 +266,6 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: => val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) - println(isym) - println(ctorParams) val ctorAuxParams = auxParams.map(ps => ps.params.map(p => p.sym -> scope.allocateName(p.sym))) def mkMethods(mtds: Ls[FunDefn], mtdPrefix: Str)(using Scope): Document = diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index fd937162f7..35f72c8f47 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -9,77 +9,33 @@ :todo :noSanityCheck +class Eff +//│ Elab: { Cls Eff { }; } + +:sjs :lift -:ssjs -class A with - let x = 2 - val bruh = () => x - fun f = - fun g() = - set x += 2 - this - g -let a = new A -a.f() -a.f() -a.bruh() -//│ Elab: { Cls A { let term:class:A‹690›.x‹692›; term:class:A‹690›.x‹692› = 2; method val member:bruh‹688› = λ(). term:class:A‹690›.x‹692›#666; method fun member:f‹687› = { fun member:g‹686›() = { term:class:A‹690›.x‹692›#666 := builtin:+‹48›#0(term:class:A‹690›.x‹692›#666, 2); class:A‹690›#0 }; member:g‹686›#666 }; }; let a‹703›; a‹703› = new member:A‹689›#666; a‹703›#666.f(); a‹703›#666.f(); a‹703›#666.bruh() } -//│ JS: -//│ let g, A1, a, lambda, tmp, tmp1, Capture$A1, lambda$, g$; -//│ g$ = function g$(A$cap, A2) { +fun f() = + fun g() = + let x = 1 + fun h() = + x + h + g() +//│ Elab: { fun member:f‹693›() = { fun member:g‹692›() = { let x‹696›; x‹696› = 1; fun member:h‹691›() = x‹696›#666; member:h‹691›#666 }; member:g‹692›#666() }; } +//│ JS (unsanitized): +//│ let h, g, f, h$; +//│ h$ = function h$(x) { //│ return () => { -//│ return g(A$cap, A2) +//│ return h(x) //│ } //│ }; -//│ g = function g(A$cap, A2) { -//│ let tmp2; -//│ tmp2 = A$cap.x$0 + 2; -//│ A$cap.x$0 = tmp2; -//│ return A2 +//│ h = function h(x) { +//│ return x //│ }; -//│ lambda$ = (undefined, function (A$cap) { -//│ return () => { -//│ return lambda(A$cap) -//│ } -//│ }); -//│ lambda = (undefined, function (A$cap) { -//│ return A$cap.x$0 -//│ }); -//│ (class Capture$A { -//│ static { -//│ Capture$A1 = this -//│ } -//│ constructor(x$0) { -//│ this.x$0 = x$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Capture$A"]; -//│ }); -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() { -//│ this.A$cap = new Capture$A1(undefined); -//│ let lambda$here; -//│ this.A$cap.x$0 = 2; -//│ lambda$here = lambda$(this.A$cap); -//│ this.bruh = lambda$here; -//│ } -//│ #A$cap; -//│ #x; -//│ get f() { -//│ let g$here; -//│ g$here = g$(this.A$cap, this); -//│ return g$here; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); -//│ a = globalThis.Object.freeze(new A1()); -//│ tmp = runtime.safeCall(a.f()); -//│ tmp1 = runtime.safeCall(a.f()); -//│ block$res1 = runtime.safeCall(a.bruh()); -//│ undefined -//│ = 6 -//│ a = A { A$cap: Capture$A { x$0: 6 }, bruh: fun } +//│ g = function g() { +//│ let x, h$here; +//│ x = 1; +//│ h$here = h$(x); +//│ return h$here +//│ }; +//│ f = function f() { return g() }; diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls new file mode 100644 index 0000000000..efb7527ad5 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -0,0 +1,59 @@ +:js +// :de +// :sjs +// :pt +// :elt + +:global +:d +:todo +:noSanityCheck + +class Eff +//│ Elab: { Cls Eff { }; } + +// function call and defn inside handler +:sjs +:lift +fun f = + fun g = + let y = 2 + fun h() = set y += 1 + h +//│ Elab: { fun member:f‹693› = { fun member:g‹692› = { let y‹696›; y‹696› = 2; fun member:h‹691›() = y‹696›#666 := builtin:+‹48›#0(y‹696›#666, 1); member:h‹691›#666 }; () }; } +//│ JS (unsanitized): +//│ let h, g, f, g$, Capture$scope11, h$; +//│ (class Capture$scope1 { +//│ static { +//│ Capture$scope11 = this +//│ } +//│ constructor(y$0) { +//│ this.y$0 = y$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$scope1"]; +//│ }); +//│ h$ = function h$(scope1$cap) { +//│ return () => { +//│ return h(scope1$cap) +//│ } +//│ }; +//│ h = function h(scope1$cap) { +//│ let tmp; +//│ tmp = scope1$cap.y$0 + 1; +//│ scope1$cap.y$0 = tmp; +//│ return runtime.Unit +//│ }; +//│ g$ = function g$() { +//│ return () => { +//│ return g() +//│ } +//│ }; +//│ g = function g() { +//│ let y, scope1$cap, h$here; +//│ scope1$cap = new Capture$scope11(y); +//│ scope1$cap.y$0 = 2; +//│ h$here = h$(scope1$cap); +//│ return h$here +//│ }; +//│ f = function f() { return runtime.Unit }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index d0f005f9ce..76eb8cfcef 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -90,112 +90,69 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f6, tmp5, Bad$, Good$, f$capture3; -//│ Good$ = function Good$(isMut, x, y, z, f$capture4) { -//│ let tmp6, tmp7; -//│ if (isMut === true) { -//│ tmp6 = new Good1.class(); -//│ } else { -//│ tmp6 = globalThis.Object.freeze(new Good1.class()); +//│ let Bad1, Good1, f6, tmp5, Capture$scope01; +//│ (class Capture$scope0 { +//│ static { +//│ Capture$scope01 = this //│ } -//│ tmp7 = tmp6(x, y, z, f$capture4); -//│ return tmp7 -//│ }; -//│ Good1 = function Good() { -//│ return (x, y, z, f$capture4) => { -//│ return globalThis.Object.freeze(new Good.class()(x, y, z, f$capture4)); +//│ constructor(z$0, w$1) { +//│ this.w$1 = w$1; +//│ this.z$0 = z$0; //│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$scope0"]; +//│ }); +//│ Good1 = function Good(scope0$cap, x, y) { +//│ return globalThis.Object.freeze(new Good.class(scope0$cap, x, y)); //│ }; //│ (class Good { //│ static { //│ Good1.class = this //│ } -//│ constructor() { -//│ return (x, y, z, f$capture4) => { -//│ this.f$capture = f$capture4; -//│ this.x = x; -//│ this.y = y; -//│ this.z = z; -//│ return this; -//│ } +//│ constructor(scope0$cap, x, y) { +//│ this.scope0$cap = scope0$cap; +//│ this.x = x; +//│ this.y = y; //│ } -//│ #f$capture; -//│ #x; -//│ #y; -//│ #z; -//│ get f$capture() { return this.#f$capture; } -//│ set f$capture(value) { this.#f$capture = value; } -//│ get x() { return this.#x; } -//│ set x(value) { this.#x = value; } -//│ get y() { return this.#y; } -//│ set y(value) { this.#y = value; } -//│ get z() { return this.#z; } -//│ set z(value) { this.#z = value; } +//│ #Good$cap; //│ foo() { //│ let tmp6, tmp7; -//│ this.z = 100; +//│ this.scope0$cap.z$0 = 100; //│ tmp6 = this.x + this.y; -//│ tmp7 = tmp6 + this.z; -//│ return tmp7 + this.f$capture.w$capture$0 +//│ tmp7 = tmp6 + this.scope0$cap.z$0; +//│ return tmp7 + this.scope0$cap.w$1 //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Good", []]; +//│ static [definitionMetadata] = ["class", "Good", [null, null, null]]; //│ }); -//│ Bad$ = function Bad$(isMut, f$capture4) { -//│ let tmp6, tmp7; -//│ if (isMut === true) { -//│ tmp6 = new Bad1.class(); -//│ } else { -//│ tmp6 = globalThis.Object.freeze(new Bad1.class()); -//│ } -//│ tmp7 = tmp6(f$capture4); -//│ return tmp7 -//│ }; -//│ Bad1 = function Bad() { -//│ return (f$capture4) => { -//│ return globalThis.Object.freeze(new Bad.class()(f$capture4)); -//│ } +//│ Bad1 = function Bad(scope0$cap) { +//│ return globalThis.Object.freeze(new Bad.class(scope0$cap)); //│ }; //│ (class Bad { //│ static { //│ Bad1.class = this //│ } -//│ constructor() { -//│ return (f$capture4) => { -//│ this.f$capture = f$capture4; -//│ return this; -//│ } +//│ constructor(scope0$cap) { +//│ this.scope0$cap = scope0$cap; //│ } -//│ #f$capture; -//│ get f$capture() { return this.#f$capture; } -//│ set f$capture(value) { this.#f$capture = value; } +//│ #Bad$cap; //│ foo() { -//│ this.f$capture.w$capture$0 = 10000; +//│ this.scope0$cap.w$1 = 10000; //│ return runtime.Unit //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Bad", []]; -//│ }); -//│ (class f$capture2 { -//│ static { -//│ f$capture3 = this -//│ } -//│ constructor(w$capture$0) { -//│ this.w$capture$0 = w$capture$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "f$capture"]; +//│ static [definitionMetadata] = ["class", "Bad", [null]]; //│ }); //│ f6 = function f() { -//│ let x, y, z, w, tmp6, tmp7, capture; -//│ capture = new f$capture3(null); +//│ let x, y, z, w, tmp6, tmp7, scope0$cap; +//│ scope0$cap = new Capture$scope01(z, w); //│ x = 1; //│ y = 10; -//│ z = 10; -//│ capture.w$capture$0 = 1000; -//│ tmp6 = Bad$(false, capture); +//│ scope0$cap.z$0 = 10; +//│ scope0$cap.w$1 = 1000; +//│ tmp6 = Bad1(scope0$cap); //│ tmp7 = tmp6.foo(); -//│ return Good$(false, x, y, z, capture) +//│ return Good1(scope0$cap, x, y) //│ }; //│ tmp5 = f6(); //│ runtime.safeCall(tmp5.foo()) @@ -216,8 +173,8 @@ let b = a.newA() b.foo() a.getX() //│ = 2 -//│ a = A() -//│ b = A() +//│ a = A(_) +//│ b = A(_) // handler @@ -239,11 +196,12 @@ fun f() = fun foo() = Test foo()(0) f().get() -//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 0 +//│ FAILURE: Unexpected lack of warnings :lift :w +:expect 2 fun f(x) = class Test() with fun get() = @@ -252,8 +210,23 @@ fun f(x) = let foo = Test foo() f(2).get() -//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. -//│ = 2 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//│ at Runtime.checkCall (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:629:41) +//│ at Test10.get (REPL42:1:748) +//│ at REPL42:1:1115 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ FAILURE: Unexpected lack of warnings :expect 2 fun test() = diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls index 4a38ce4a73..71e407799d 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls @@ -9,10 +9,60 @@ fun foo(x) = module C with val empty = C(123) C.empty -//│ ═══[WARNING] Modules are not yet lifted. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.9: module C with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: scala.collection.immutable.List.map(List.scala:236) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) foo(10).get -//│ = [10, 123] +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:foo +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $block$res -> block$res1, $Block -> Block, $tmp -> tmp, $Shape -> Shape, $selRes -> selRes, $block$res -> block$res, $discarded -> discarded, member:Predef -> Predef) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.28: foo(10).get +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.6: fun foo(x) = +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.7: class C(y) with +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.8: fun get = [x, y] +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.9: module C with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.10: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ at REPL10:1:48 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) :w fun foo(x) = @@ -21,9 +71,59 @@ fun foo(x) = module C with val empty = new C C.empty -//│ ═══[WARNING] Modules are not yet lifted. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.71: module C with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: scala.collection.immutable.List.map(List.scala:236) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) foo(10).get -//│ = 10 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:foo +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res2, $tmp -> tmp1, $runtime -> runtime, $selRes -> selRes1, $definitionMetadata -> definitionMetadata, $discarded -> discarded1, $prettyPrint -> prettyPrint, $Term -> Term, $block$res -> block$res1, $Block -> Block, $tmp -> tmp, $Shape -> Shape, $selRes -> selRes, $block$res -> block$res, $discarded -> discarded, member:Predef -> Predef) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.90: foo(10).get +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.68: fun foo(x) = +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.69: class C with +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.70: fun get = x +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.71: module C with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.72: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ at REPL13:1:52 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index cf57dc78d0..9bcc79442d 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -9,27 +9,20 @@ fun f(x) = fun get = x module A fun g = new A -//│ ═══[WARNING] Modules are not yet lifted. -//│ JS (unsanitized): -//│ let f, g$; -//│ g$ = function g$(A$member, A1, x) { -//│ return globalThis.Object.freeze(new A$member()) -//│ }; -//│ f = function f(x) { -//│ let A1; -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() {} -//│ get get() { -//│ return x; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); -//│ return runtime.Unit -//│ }; -//│ ═══[WARNING] Modules are not yet lifted. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.10: module A +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: scala.collection.immutable.List.map(List.scala:236) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 8adf8005d3..cda81b5bc7 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -7,41 +7,24 @@ data class A(x) with fun getB() = x + y fun getA() = B(2).getB() //│ JS (unsanitized): -//│ let B1, A1, B$; -//│ B$ = function B$(isMut, A$instance, y) { -//│ let tmp, tmp1; -//│ if (isMut === true) { -//│ tmp = new B1.class(y); -//│ } else { -//│ tmp = globalThis.Object.freeze(new B1.class(y)); -//│ } -//│ tmp1 = tmp(A$instance); -//│ return tmp1 -//│ }; -//│ B1 = function B(y) { -//│ return (A$instance) => { -//│ return globalThis.Object.freeze(new B.class(y)(A$instance)); -//│ } +//│ let B1, A1; +//│ B1 = function B(A2, y) { +//│ return globalThis.Object.freeze(new B.class(A2, y)); //│ }; //│ (class B { //│ static { //│ B1.class = this //│ } -//│ constructor(y) { -//│ return (A$instance) => { -//│ this.A$instance = A$instance; -//│ this.y = y; -//│ return this; -//│ } +//│ constructor(A2, y) { +//│ this.A = A2; +//│ this.y = y; //│ } -//│ #A$instance; -//│ get A$instance() { return this.#A$instance; } -//│ set A$instance(value) { this.#A$instance = value; } +//│ #B$cap; //│ getB() { -//│ return this.A$instance.x + this.y +//│ return this.A.x + this.y //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "B", ["y"]]; +//│ static [definitionMetadata] = ["class", "B", [null, "y"]]; //│ }); //│ A1 = function A(x) { //│ return globalThis.Object.freeze(new A.class(x)); @@ -53,9 +36,10 @@ data class A(x) with //│ constructor(x) { //│ this.x = x; //│ } +//│ #A$cap; //│ getA() { //│ let tmp; -//│ tmp = B$(false, this, 2); +//│ tmp = B1(this, 2); //│ return tmp.getB() //│ } //│ toString() { return runtime.render(this); } @@ -74,26 +58,40 @@ class A with (new A).x() //│ JS (unsanitized): //│ let g, A3, tmp1, g$; -//│ g$ = function g$(A$instance) { -//│ return 2 -//│ }; -//│ g = function g(A$instance) { +//│ g$ = function g$() { //│ return () => { -//│ return g$(A$instance) +//│ return g() //│ } //│ }; +//│ g = function g() { +//│ return 2 +//│ }; //│ (class A2 { //│ static { //│ A3 = this //│ } //│ constructor() { -//│ let g$here; -//│ g$here = runtime.safeCall(g(this)); -//│ this.x = g$here; +//│ this.x = g; //│ } +//│ #A$cap; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); //│ tmp1 = globalThis.Object.freeze(new A3()); //│ runtime.safeCall(tmp1.x()) //│ = 2 + +class A with + let x = 2 + val bruh = () => x + fun f = + fun g() = + set x += 2 + this + g +let a = new A +a.f() +a.f() +a.bruh() +//│ = 6 +//│ a = A { A$cap: Capture$A { x$0: 6 }, bruh: fun } diff --git a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls index e628fd9870..1904673708 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls @@ -8,6 +8,23 @@ module A with data class Test with f() val a = 1 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.7: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1471) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1470) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :effectHandlers class Test with diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 1df91fb01e..853cfff39b 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -93,12 +93,12 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let g4, f4, g$4; -//│ g$4 = function g$(a3, a4, a5, a2, a6, a1) { +//│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ return g4(a3, a4, a5, a2, a6, a1) +//│ return g4(a1, a2, a3, a4, a5, a6) //│ } //│ }; -//│ g4 = function g(a3, a4, a5, a2, a6, a1) { +//│ g4 = function g(a1, a2, a3, a4, a5, a6) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = a1 + a2; //│ tmp2 = tmp1 + a3; @@ -128,18 +128,12 @@ let x = f2().toString() f1() let y = f2().toString() x + y -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1252) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1312) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1311) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ = "01" +//│ f1 = fun +//│ f2 = fun +//│ ret = Tuple(fun, fun) +//│ x = "0" +//│ y = "1" data class Tuple(a, b) @@ -152,18 +146,6 @@ fun f(used1) = Tuple(h, i) fun g22() = used1 Tuple(g1(10), g22) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1252) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1312) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1311) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :expect "11110110" let ret = f(1) @@ -177,55 +159,16 @@ hFun() let c = g2() let d = iFun() a.toString() + b + c + d -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:f -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res7, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, member:g$ -> g$4, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, $block$res -> block$res5, member:g$ -> g$2, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g -> g3, member:f -> f3, $block$res -> block$res1, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $tmp -> tmp, member:bar$ -> bar$1, member:g -> g, member:f -> f, $block$res -> block$res6, member:g$ -> g$3, member:Predef‹726› -> Predef, member:g -> g4, member:f -> f4, $block$res -> block$res3, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//│ ║ l.169: let ret = f(1) -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.146: fun f(used1) = -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.147: fun g1(used2) = -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.148: fun h() = -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.149: set used1 = 10 -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.150: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 -//│ at Runtime.checkArgs (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:615:41) -//│ at f (REPL16:1:535) -//│ at REPL31:1:180 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '"11110110"', got: 'undefined' -//│ a = undefined -//│ b = undefined -//│ c = undefined -//│ d = undefined -//│ g2 = undefined -//│ gRet = undefined -//│ hFun = undefined -//│ iFun = undefined -//│ ret = undefined +//│ = "11110110" +//│ a = 11 +//│ b = 1 +//│ c = 10 +//│ d = 110 +//│ g2 = fun +//│ gRet = Tuple(fun, fun) +//│ hFun = fun +//│ iFun = fun +//│ ret = Tuple(Tuple(fun, fun), fun) // some variables mutated, some not :sjs @@ -239,27 +182,27 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let g5, h, f5, Capture$f1, h$, g$5; -//│ g$5 = function g$(f$cap, immutable) { +//│ let g6, h2, f7, Capture$f3, h$2, g$6; +//│ g$6 = function g$(f$cap, immutable) { //│ return () => { -//│ return g5(f$cap, immutable) +//│ return g6(f$cap, immutable) //│ } //│ }; -//│ h$ = function h$(f$cap) { +//│ h$2 = function h$(f$cap) { //│ return () => { -//│ return h(f$cap) +//│ return h2(f$cap) //│ } //│ }; -//│ g5 = function g(f$cap, immutable) { +//│ g6 = function g(f$cap, immutable) { //│ f$cap.mutated$0 = 2; //│ return immutable + f$cap.mutated$0 //│ }; -//│ h = function h(f$cap) { +//│ h2 = function h(f$cap) { //│ return f$cap.mutated$0 //│ }; -//│ (class Capture$f { +//│ (class Capture$f2 { //│ static { -//│ Capture$f1 = this +//│ Capture$f3 = this //│ } //│ constructor(mutated$0) { //│ this.mutated$0 = mutated$0; @@ -267,15 +210,15 @@ f(1, 2, 1000) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$f"]; //│ }); -//│ f5 = function f(unused, immutable, mutated) { -//│ let a1, tmp5, tmp6, f$cap; -//│ f$cap = new Capture$f1(undefined); -//│ a1 = g5(f$cap, immutable); -//│ tmp5 = h(f$cap); -//│ tmp6 = a1 + tmp5; -//│ return tmp6 + unused +//│ f7 = function f(unused, immutable, mutated) { +//│ let a1, tmp8, tmp9, f$cap; +//│ f$cap = new Capture$f3(mutated); +//│ a1 = g6(f$cap, immutable); +//│ tmp8 = h2(f$cap); +//│ tmp9 = a1 + tmp8; +//│ return tmp9 + unused //│ }; -//│ f5(1, 2, 1000) +//│ f7(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -337,56 +280,11 @@ fun g() = x f g()(1) -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = k -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, $block$res -> block$res5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ curThis = N -//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1) -//│ curThis = S of N -//│ bindings = HashMap(x -> x, scope0$cap -> scope0$cap) -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'k' -//│ ║ l.329: let k = 4 -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.329: let k = 4 -//│ ╙── ^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = scope0$cap -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $prettyPrint -> prettyPrint, $discarded -> discarded2, $Term -> Term, $selRes -> selRes3, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:g -> g5, member:h -> h, member:f -> f5, $block$res -> block$res5, member:g$ -> g$2, member:g -> g3, member:f -> f3, $block$res -> block$res9, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:g$ -> g$6, member:g -> g7, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:g$ -> g$, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ curThis = N -//│ bindings = HashMap(member:h$ -> h$3, member:h -> h3, member:f -> f12, member:g -> g11, $tmp -> tmp5, class:Capture$scope0 -> Capture$scope04, member:Capture$scope0 -> Capture$scope05, member:f$ -> f$1) -//│ curThis = S of N -//│ bindings = HashMap(x -> x) -//│ curThis = N -//│ bindings = HashMap(k -> k) -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' //│ JS (unsanitized): -//│ let h3, f12, g11, tmp5, Capture$scope05, f$1, h$3; -//│ (class Capture$scope04 { +//│ let h5, f15, g13, tmp8, Capture$scope07, f$1, h$5; +//│ (class Capture$scope06 { //│ static { -//│ Capture$scope05 = this +//│ Capture$scope07 = this //│ } //│ constructor(y$0) { //│ this.y$0 = y$0; @@ -394,48 +292,37 @@ g()(1) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); -//│ h$3 = function h$(scope0$cap, x) { +//│ h$5 = function h$(scope0$cap, x1, k) { //│ return () => { -//│ return h3(scope0$cap, x) +//│ return h5(scope0$cap, x1, k) //│ } //│ }; -//│ h3 = function h(scope0$cap, x) { +//│ h5 = function h(scope0$cap, x1, k) { //│ k = 5; -//│ x = 4; -//│ return x + scope0$cap.y$0 +//│ x1 = 4; +//│ return x1 + scope0$cap.y$0 //│ }; -//│ f$1 = function f$() { -//│ return (x) => { -//│ return f12(x) +//│ f$1 = function f$(scope0$cap) { +//│ return (x1) => { +//│ return f15(scope0$cap, x1) //│ } //│ }; -//│ f12 = function f(x) { +//│ f15 = function f(scope0$cap, x1) { //│ let k; //│ k = 4; //│ scope0$cap.y$0 = 2; -//│ return x +//│ return x1 //│ }; -//│ g11 = function g() { -//│ let y, scope0$cap; -//│ scope0$cap = new Capture$scope05(undefined); +//│ g13 = function g() { +//│ let y1, scope0$cap, f$here; +//│ scope0$cap = new Capture$scope07(y1); //│ scope0$cap.y$0 = 0; -//│ return f12 +//│ f$here = f$1(scope0$cap); +//│ return f$here //│ }; -//│ tmp5 = g11(); -//│ runtime.safeCall(tmp5(1)) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: scope0$cap is not defined -//│ at f (REPL61:1:1065) -//│ at REPL61:1:1345 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ tmp8 = g13(); +//│ runtime.safeCall(tmp8(1)) +//│ = 1 :expect 2 fun f() = @@ -463,56 +350,7 @@ fun f(x, y, z) = set z = 3 g f(0, 0, 0)() -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = f$cap -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ curThis = S of N -//│ bindings = HashMap($args -> args) -//│ curThis = N -//│ bindings = HashMap() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$cap' -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = f$cap -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:g$ -> g$7, member:g -> g8, member:h -> h1, member:f -> f8, member:g$ -> g$4, $block$res -> block$res12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ curThis = S of N -//│ bindings = HashMap($args -> args) -//│ curThis = N -//│ bindings = HashMap() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'f$cap' -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: f$cap is not defined -//│ at h (REPL68:1:979) -//│ at g (REPL68:1:834) -//│ at REPL68:1:266 -//│ at REPL68:1:1774 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '6', got: 'undefined' +//│ = 6 fun f(x, cond) = set x = 1 @@ -531,39 +369,7 @@ fun f() = f() x f() -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = scope0$cap -//│ _2 = class hkmc2.semantics.VarSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $block$res -> block$res8, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $runtime -> runtime, $discarded -> discarded1, $definitionMetadata -> definitionMetadata, member:g$ -> g$1, $selRes -> selRes2, $block$res -> block$res15, $prettyPrint -> prettyPrint, $discarded -> discarded2, $tmp -> tmp5, $Term -> Term, $selRes -> selRes3, class:Capture$scope0 -> Capture$scope04, $Block -> Block, $discarded -> discarded3, $Shape -> Shape, $tmp -> tmp1, member:g -> g2, $tmp -> tmp2, member:f -> f2, $tmp -> tmp3, $tmp -> tmp4, member:Capture$scope0 -> Capture$scope05, member:g -> g5, member:h -> h, member:f -> f5, member:f$ -> f$1, member:h$ -> h$3, member:g -> g12, member:f -> f13, $block$res -> block$res5, ret -> ret1, $block$res -> block$res16, member:g$ -> g$2, $tmp -> tmp6, class:Capture$scope0 -> Capture$scope06, member:g -> g3, member:Capture$scope0 -> Capture$scope07, member:f -> f3, member:g$ -> g$10, member:g -> g13, $block$res -> block$res9, member:h -> h4, member:i -> i, member:f -> f14, class:Capture$f -> Capture$f, member:Capture$f -> Capture$f1, member:h$ -> h$, member:g$ -> g$5, $block$res -> block$res6, member:g -> g6, member:f -> f6, member:g$ -> g$3, $block$res -> block$res17, $tmp -> tmp7, class:Capture$f -> Capture$f2, member:g -> g4, member:f -> f4, $block$res -> block$res10, class:Capture$scope0 -> Capture$scope0, member:Capture$scope0 -> Capture$scope01, member:Capture$f -> Capture$f3, member:i$ -> i$, member:g$ -> g$6, member:h$ -> h$4, member:g -> g7, member:g$ -> g$11, member:f -> f7, member:f -> f15, $block$res -> block$res18, member:lambda -> lambda, $block$res -> block$res11, class:Capture$scope0 -> Capture$scope02, member:lambda -> lambda1, member:lambda$ -> lambda$, member:Capture$scope0 -> Capture$scope03, $block$res -> block$res7, member:lambda$ -> lambda$1, member:g$ -> g$7, member:f -> f16, member:g -> g8, member:g -> g14, member:h -> h1, member:f -> f17, member:f -> f8, member:g$ -> g$4, $block$res -> block$res19, class:Capture$scope0 -> Capture$scope08, member:Capture$scope0 -> Capture$scope09, member:f$ -> f$2, $block$res -> block$res12, member:g$ -> g$12, member:h$ -> h$1, member:g$ -> g$8, $block$res -> block$res, member:bar -> bar, member:foo -> foo, member:g_h -> g_h, member:f -> f9, $block$res -> block$res1, member:g -> g9, member:f -> f10, member:bar$ -> bar$, member:bar -> bar1, member:foo -> foo1, $block$res -> block$res2, $block$res -> block$res13, $tmp -> tmp, member:f$ -> f$, member:bar$ -> bar$1, member:g$ -> g$9, member:g -> g, member:f -> f, member:h -> h2, member:f -> f11, member:g -> g10, member:Predef‹726› -> Predef, $block$res -> block$res14, $block$res -> block$res3, member:h$ -> h$2, member:h -> h3, member:f -> f12, member:g$ -> g$, member:g -> g11, member:g -> g1, member:f -> f1, ret -> ret, gRet -> gRet, g2 -> g21, hFun -> hFun, iFun -> iFun, a -> a, b -> b, c -> c, d -> d) -//│ curThis = S of N -//│ bindings = HashMap($args -> args) -//│ curThis = N -//│ bindings = HashMap() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'scope0$cap' -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: scope0$cap is not defined -//│ at f (REPL74:1:862) -//│ at f (REPL74:1:1106) -//│ at REPL74:1:1173 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' +//│ = 1 let n = 0 //│ n = 0 @@ -581,18 +387,6 @@ fun f(es, vs) = class A with val fld = v' new A -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1250) -//│ at: hkmc2.Lifter.$anonfun$50(Lifter.scala:1281) -//│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1281) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1301) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:923) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:930) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:629) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) // try to avoid creating multiple closures @@ -611,26 +405,26 @@ fun f(x) = set y = 2 [g, g] //│ JS (unsanitized): -//│ let g17, f20, g$15; -//│ g$15 = function g$(y) { +//│ let g19, f25, g$16; +//│ g$16 = function g$(y1) { //│ return () => { -//│ return g17(y) +//│ return g19(y1) //│ } //│ }; -//│ g17 = function g(y) { -//│ return y +//│ g19 = function g(y1) { +//│ return y1 //│ }; -//│ f20 = function f(x) { -//│ let y, scrut, g$here; -//│ scrut = x < 0; +//│ f25 = function f(x1) { +//│ let y1, scrut, g$here; +//│ scrut = x1 < 0; //│ if (scrut === true) { -//│ y = 1; -//│ g$here = g$15(y); +//│ y1 = 1; +//│ g$here = g$16(y1); //│ return globalThis.Object.freeze([ //│ g$here, //│ g$here //│ ]) -//│ } else { y = 2; g$here = g$15(y); return globalThis.Object.freeze([ g$here, g$here ]) } +//│ } else { y1 = 2; g$here = g$16(y1); return globalThis.Object.freeze([ g$here, g$here ]) } //│ }; :sjs @@ -640,18 +434,18 @@ fun f(x) = set x += 1 [a, g] //│ JS (unsanitized): -//│ let g18, f21, Capture$f5, g$16; -//│ g$16 = function g$(f$cap) { +//│ let g20, f26, Capture$f7, g$17; +//│ g$17 = function g$(f$cap) { //│ return () => { -//│ return g18(f$cap) +//│ return g20(f$cap) //│ } //│ }; -//│ g18 = function g(f$cap) { +//│ g20 = function g(f$cap) { //│ return f$cap.x$0 //│ }; -//│ (class Capture$f4 { +//│ (class Capture$f6 { //│ static { -//│ Capture$f5 = this +//│ Capture$f7 = this //│ } //│ constructor(x$0) { //│ this.x$0 = x$0; @@ -659,14 +453,14 @@ fun f(x) = //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$f"]; //│ }); -//│ f21 = function f(x) { -//│ let a1, tmp8, f$cap, g$here; -//│ f$cap = new Capture$f5(undefined); -//│ g$here = g$16(f$cap); +//│ f26 = function f(x1) { +//│ let a1, tmp11, f$cap, g$here; +//│ f$cap = new Capture$f7(x1); +//│ g$here = g$17(f$cap); //│ a1 = g$here; -//│ tmp8 = f$cap.x$0 + 1; -//│ f$cap.x$0 = tmp8; -//│ g$here = g$16(f$cap); +//│ tmp11 = f$cap.x$0 + 1; +//│ f$cap.x$0 = tmp11; +//│ g$here = g$17(f$cap); //│ return globalThis.Object.freeze([ a1, g$here ]) //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls index 9e11d7207d..9e2c1d43f4 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -6,23 +6,21 @@ import "../../mlscript-compile/Option.mls" :sjs module A with fun f(x) = x is Option.Some -//│ JS (unsanitized): -//│ let A1; -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() { -//│ runtime.Unit; -//│ } -//│ static f(x) { -//│ if (x instanceof Option.Some.class) { -//│ return true -//│ } else { -//│ return false -//│ } -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.7: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1471) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1470) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) diff --git a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls index cbffcbd5df..1dbf43a838 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls @@ -11,27 +11,12 @@ fun foo() = while x < 5 do set x += 1 fs.push of () => x -//│ /!!!\ Uncaught error: java.lang.AssertionError: assertion failed: loops should be rewritten to functions before scope flattening :expect 5 foo() fs.0() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.17: foo() -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.9: fun foo() = -//│ ║ ^^^^^^^^^^^ -//│ ║ l.10: let x = 1 -//│ ║ ^^^^^^^^^^^ -//│ ║ l.11: while x < 5 do -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.12: set x += 1 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.13: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ ═══[RUNTIME ERROR] Expected: '5', got: 'undefined' +//│ ═══[RUNTIME ERROR] Expected: '5', got: '2' +//│ = 2 let fs = mut [] @@ -43,7 +28,6 @@ fun foo() = let x = i set i += 1 fs.push of () => x -//│ /!!!\ Uncaught error: java.lang.AssertionError: assertion failed: loops should be rewritten to functions before scope flattening // * Note that this works with while loop rewriting // * See [fixme:0] for cause of the issue @@ -51,22 +35,7 @@ fun foo() = :expect 1 foo() fs.0() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.52: foo() -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.40: fun foo() = -//│ ║ ^^^^^^^^^^^ -//│ ║ l.41: let i = 1 -//│ ║ ^^^^^^^^^^^ -//│ ║ l.42: while i < 5 do -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.43: let x = i -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.44: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' +//│ = 1 :sjs @@ -75,25 +44,36 @@ fun foo() = while true do set x += 1 return () => x -//│ /!!!\ Uncaught error: java.lang.AssertionError: assertion failed: loops should be rewritten to functions before scope flattening +//│ JS (unsanitized): +//│ let foo2, lambda2, lambda$2; +//│ lambda$2 = (undefined, function (x) { +//│ return () => { +//│ return lambda2(x) +//│ } +//│ }); +//│ lambda2 = (undefined, function (x) { +//│ return x +//│ }); +//│ foo2 = function foo() { +//│ let x, tmp2; +//│ x = 1; +//│ lbl: while (true) { +//│ let scrut, tmp3; +//│ scrut = true; +//│ if (scrut === true) { +//│ let lambda$here; +//│ tmp3 = x + 1; +//│ x = tmp3; +//│ lambda$here = lambda$2(x); +//│ return lambda$here +//│ } else { tmp2 = runtime.Unit; } +//│ break; +//│ } +//│ return tmp2 +//│ }; :expect 2 foo()() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.81: foo()() -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.73: fun foo() = -//│ ║ ^^^^^^^^^^^ -//│ ║ l.74: let x = 1 -//│ ║ ^^^^^^^^^^^ -//│ ║ l.75: while true do -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.76: set x += 1 -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.77: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls index e7eff12d2f..6d04f22445 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls @@ -12,32 +12,32 @@ fun foo() = xs.push(bar) set x = 2 //│ JS (unsanitized): -//│ let bar, foo, bar$, foo$capture1; -//│ bar$ = function bar$(foo$capture2) { -//│ return foo$capture2.x$capture$0 -//│ }; -//│ bar = function bar(foo$capture2) { -//│ return () => { -//│ return bar$(foo$capture2) -//│ } -//│ }; -//│ (class foo$capture { +//│ let bar, foo, Capture$scope01, bar$; +//│ (class Capture$scope0 { //│ static { -//│ foo$capture1 = this +//│ Capture$scope01 = this //│ } -//│ constructor(x$capture$0) { -//│ this.x$capture$0 = x$capture$0; +//│ constructor(x$0) { +//│ this.x$0 = x$0; //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "foo$capture"]; +//│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); +//│ bar$ = function bar$(scope0$cap) { +//│ return () => { +//│ return bar(scope0$cap) +//│ } +//│ }; +//│ bar = function bar(scope0$cap) { +//│ return scope0$cap.x$0 +//│ }; //│ foo = function foo() { -//│ let x, tmp, capture, bar$here; -//│ capture = new foo$capture1(null); -//│ capture.x$capture$0 = 1; -//│ bar$here = runtime.safeCall(bar(capture)); +//│ let x, tmp, scope0$cap, bar$here; +//│ scope0$cap = new Capture$scope01(x); +//│ scope0$cap.x$0 = 1; +//│ bar$here = bar$(scope0$cap); //│ tmp = runtime.safeCall(xs.push(bar$here)); -//│ capture.x$capture$0 = 2; +//│ scope0$cap.x$0 = 2; //│ return runtime.Unit //│ }; @@ -99,6 +99,13 @@ fun foo() = x bar //│ JS (unsanitized): -//│ let bar4, foo4; bar4 = function bar() { let x; return x }; foo4 = function foo() { return bar4 }; +//│ let bar4, foo4, bar$4; +//│ bar$4 = function bar$() { +//│ return () => { +//│ return bar4() +//│ } +//│ }; +//│ bar4 = function bar() { let x; return x }; +//│ foo4 = function foo() { return bar4 }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls index 2487471824..816be05ef5 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls @@ -7,6 +7,6 @@ fun foo() = pattern P = Int x => x is P -//│ ═══[WARNING] Cannot yet lift class `P` as it is used as a first-class class. +//│ FAILURE: Unexpected lack of warnings From c4aeafacd86045a1fa36b655d6332c3dadc74f34 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 16 Jan 2026 21:55:48 +0800 Subject: [PATCH 09/18] run handler tests, fix some things --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 420 +------------- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 3 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 287 +++++++++- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 54 +- .../src/test/mlscript/backlog/Lifter.mls | 92 ++- .../codegen/ScopedBlocksAndHandlers.mls | 14 +- .../src/test/mlscript/handlers/Debugging.mls | 43 +- .../mlscript/handlers/EffectInHandler.mls | 2 +- .../src/test/mlscript/handlers/Effects.mls | 95 +++- .../test/mlscript/handlers/EffectsHygiene.mls | 27 +- .../mlscript/handlers/EffectsInClasses.mls | 18 + .../mlscript/handlers/HandlersScratch.mls | 90 +++ .../mlscript/handlers/ManualStackSafety.mls | 522 +++++++++++++++++- .../mlscript/handlers/NonLocalReturns.mls | 89 ++- .../mlscript/handlers/RecursiveHandlers.mls | 349 ++++++------ .../mlscript/handlers/ReturnInHandler.mls | 80 ++- .../test/mlscript/handlers/StackSafety.mls | 18 +- .../mlscript/handlers/UserThreadsSafe.mls | 16 +- .../mlscript/handlers/UserThreadsUnsafe.mls | 16 +- .../mlscript/lifter/ClassWithCompanion.mls | 28 +- .../test/mlscript/lifter/CompanionsInFun.mls | 14 +- .../src/test/mlscript/lifter/DefnsInClass.mls | 7 +- .../test/mlscript/lifter/EffectHandlers.mls | 6 +- .../src/test/mlscript/lifter/FunInFun.mls | 20 +- .../src/test/mlscript/lifter/Imports.mls | 6 +- .../test/mlscript/lifter/ModulesObjects.mls | 428 ++++++++++---- .../src/test/mlscript/lifter/Mutation.mls | 9 +- .../src/test/mlscript/tailrec/TailRecOpt.mls | 11 +- 28 files changed, 1869 insertions(+), 895 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index a49d1f5522..d8a08b223c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -114,108 +114,6 @@ object Lifter: */ class Lifter(topLevelBlk: Block)(using State, Raise): import Lifter.* - - /** - * The context of the class lifter. One can create an empty context using `LifterCtx.empty`. - * - * @param defns A map from all BlockMemberSymbols to their definitions. - * @param defnsCur All definitions that are nested in the current top level definition. - * @param nestedDefns Definitions which are nested in a given definition (shallow). - * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested definitions. - * @param accessInfo Which previously defined variables/definitions could be accessed/modified by a particular definition, - * possibly through calls to other functions or by constructing a class. - * @param ignoredDefns The definitions which must not be lifted. - * @param inScopeDefns Definitions which are in scope to another definition (excluding itself and its nested definitions). - * @param modObjLocals A map from the modules and objects to the local to which it is instantiated after lifting. - * @param localCaptureSyms The symbols in a capture corresponding to a particular local. - * The `VarSymbol` is the parameter in the capture class. - * We used to also store along with it a `BlockMemberSymbol`, the field in the class, but it wasn't used. - * @param prevFnLocals Locals belonging to function definitions that have already been traversed - * @param prevClsDefns Class definitions that have already been traversed, excluding modules - * @param inScopeISyms Inner symbols that are currently in scope (and therefore don't need to be rewritten). - * @param curModules Modules that that we are currently nested in (cleared if we are lifted out) - * @param capturePaths The path to access a particular function's capture in the local scope - * @param bmsReqdInfo The (mutable) captures and (immutable) local variables each function requires - * @param ignoredBmsPaths The path to access a particular BlockMemberSymbol (for definitions which could not be lifted) - * @param localPaths The path to access a particular local (possibly belonging to a previous function) in the current scope - * @param iSymPaths The path to access a particular `innerSymbol` (possibly belonging to a previous class) in the current scope - * @param replacedDefns Ignored (unlifted) definitions that have been rewritten and need to be replaced at the definition site. - * @param firstClsFns Nested functions which are used as first-class functions. - * @param companionMap Map from companion object symbols to the corresponding regular class symbol. - */ - case class LifterCtx private ( - val defns: Map[BlockMemberSymbol, Defn] = Map.empty, - val defnsCur: Set[BlockMemberSymbol] = Set.empty, - val nestedDefns: Map[BlockMemberSymbol, List[Defn]] = Map.empty, - val usedLocals: UsedLocalsMap = UsedLocalsMap(Map.empty), - val accessInfo: Map[BlockMemberSymbol, AccessInfo] = Map.empty, - val ignoredDefns: Set[BlockMemberSymbol] = Set.empty, - val inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty, - val modObjLocals: Map[BlockMemberSymbol, Local] = Map.empty, - val localCaptureSyms: Map[Local, VarSymbol] = Map.empty, - val prevFnLocals: FreeVars = FreeVars.empty, - val prevClsDefns: List[ClsLikeDefn] = Nil, - val inScopeISyms: Set[InnerSymbol] = Set.empty, - val curModules: List[ClsLikeDefn] = Nil, - val capturePaths: Map[BlockMemberSymbol, LocalPath] = Map.empty, - val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo] = Map.empty, // required captures - val ignoredBmsPaths: Map[BlockMemberSymbol, LocalPath] = Map.empty, - val localPaths: Map[Local, LocalPath] = Map.empty, - val isymPaths: Map[InnerSymbol, LocalPath] = Map.empty, - val replacedDefns: Map[BlockMemberSymbol, Defn] = Map.empty, - val firstClsFns: Set[BlockMemberSymbol] = Set.empty, - val companionMap: Map[InnerSymbol, InnerSymbol] = Map.empty, - ): - // gets the function to which a local belongs - def lookup(l: Local) = usedLocals.lookup(l) - - def getCapturePath(b: BlockMemberSymbol) = capturePaths.get(b) - def getLocalClosPath(l: Local) = lookup(l).flatMap(capturePaths.get(_)) - def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) - def getLocalPath(l: Local) = localPaths.get(l) - def resolveIsymPath(l: InnerSymbol) = getIsymPath(companionMap.getOrElse(l, l)) - def getIsymPath(l: InnerSymbol) = isymPaths.get(l) - def getIgnoredBmsPath(b: BlockMemberSymbol) = ignoredBmsPaths.get(b) - def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) - def isModOrObj(b: BlockMemberSymbol) = modObjLocals.contains(b) - def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) - def isRelevant(sym: BlockMemberSymbol) = defnsCur.contains(sym) - - def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) - def withModObjLocals(mp: Map[BlockMemberSymbol, Local]) = copy(modObjLocals = modObjLocals ++ mp) - def withDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(defns = mp) - def withDefnsCur(defns: Set[BlockMemberSymbol]) = copy(defnsCur = defns) - def withNestedDefns(mp: Map[BlockMemberSymbol, List[Defn]]) = copy(nestedDefns = mp) - def withAccesses(mp: Map[BlockMemberSymbol, AccessInfo]) = copy(accessInfo = mp) - def withInScopes(mp: Map[BlockMemberSymbol, Set[BlockMemberSymbol]]) = copy(inScopeDefns = mp) - def withFirstClsFns(fns: Set[BlockMemberSymbol]) = copy(firstClsFns = fns) - def withCompanionMap(mp: Map[InnerSymbol, InnerSymbol]) = copy(companionMap = mp) - def addFnLocals(f: FreeVars) = copy(prevFnLocals = prevFnLocals ++ f) - def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) - def addLocalCaptureSyms(m: Map[Local, VarSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) - def getBmsReqdInfo(sym: BlockMemberSymbol) = bmsReqdInfo.get(sym) - def replCapturePaths(paths: Map[BlockMemberSymbol, LocalPath]) = copy(capturePaths = paths) - def addCapturePath(src: BlockMemberSymbol, path: LocalPath) = copy(capturePaths = capturePaths + (src -> path)) - def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) - def replLocalPaths(m: Map[Local, LocalPath]) = copy(localPaths = m) - def replIgnoredBmsPaths(m: Map[BlockMemberSymbol, LocalPath]) = copy(ignoredBmsPaths = m) - def replIsymPaths(m: Map[InnerSymbol, LocalPath]) = copy(isymPaths = m) - def addLocalPaths(m: Map[Local, LocalPath]) = copy(localPaths = localPaths ++ m) - def addLocalPath(target: Local, path: LocalPath) = copy(localPaths = localPaths + (target -> path)) - def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, LocalPath]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) - def addIsymPath(isym: InnerSymbol, l: LocalPath) = copy(isymPaths = isymPaths + (isym -> l)) - def addIsymPaths(mp: Map[InnerSymbol, LocalPath]) = copy(isymPaths = isymPaths ++ mp) - def addreplacedDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(replacedDefns = replacedDefns ++ mp) - def inModule(defn: ClsLikeDefn) = copy(curModules = defn :: curModules) - def inISym(sym: InnerSymbol) = copy(inScopeISyms = inScopeISyms + sym) - def resetScope = copy(inScopeISyms = Set.empty) - def flushModules = - // called when we are lifted out while in some module, so we need to add the modules' isym paths - copy(curModules = Nil).addIsymPaths(curModules.map(d => d.isym -> LocalPath.Sym(d.sym)).toMap) - - object LifterCtx: - def empty = LifterCtx() - def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) extension (l: Local) def asLocalPath: LocalPath = LocalPath.Sym(l) @@ -245,16 +143,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): object FunSyms: def fromFun(b: BlockMemberSymbol, owner: Opt[InnerSymbol] = N) = FunSyms(b, TermSymbol.fromFunBms(b, owner)) - - // Info required for lifting a definition. - case class LiftedInfo( - val reqdCaptures: List[BlockMemberSymbol], // The mutable captures a lifted definition must take. - val reqdVars: List[Local], // The (passed by value) variables a lifted definition must take. - val reqdInnerSyms: List[InnerSymbol], // The inner symbols a lifted definition must take. - val reqdBms: List[BlockMemberSymbol], // BMS's belonging to unlifted definitions that this definition references. - val fakeCtorBms: Option[FunSyms[TermSymbol]], // only for classes - val singleCallBms: FunSyms[TermSymbol], // optimization - ) case class Lifted[+T <: Defn]( val liftedDefn: T, @@ -396,76 +284,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): LifterMetadata(ignored ++ newUnliftable, modules, firstClsFns) - extension (b: Block) - private def floatOut(ctx: LifterCtx) = - b.extractDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) - private def gather(ctx: LifterCtx) = - b.gatherDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) - - - def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - ??? - /* - val AccessInfo(accessed, _, refdDefns) = ctx.getAccesses(d.sym) - - val inScopeRefs = refdDefns.intersect(ctx.inScopeDefns(d.sym)) - - val includedCaptures = ctx.prevFnLocals.reqCapture - .intersect(accessed) - .map(sym => ctx.lookup(sym).get) - .toList.sortBy(_.uid) - - val refMod = inScopeRefs.intersect(ctx.modObjLocals.keySet) - val includedLocals = ((accessed -- ctx.prevFnLocals.reqCapture) ++ refMod).toList.sortBy(_.uid) - val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) - val refBms = inScopeRefs.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) - - val isModLocal = d match - case c: ClsLikeDefn if modOrObj(c) && !ctx.ignored(c.sym) => true - case _ => false - - if ctx.ignored(d.sym) || - (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then - d match - case f: FunDefn => - createLiftInfoFn(f, ctx) - case c: ClsLikeDefn => - createLiftInfoCls(c, ctx) - case _ => Map.empty - else - val fakeCtorBms = d match - case c: ClsLikeDefn if !isModLocal => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) - case _ => N - - val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) - - val info = LiftedInfo( - includedCaptures, includedLocals, clsCaptures, - refBms, fakeCtorBms.map(FunSyms.fromFun(_)), FunSyms.fromFun(singleCallBms) - ) - - d match - case f: FunDefn => - createLiftInfoFn(f, ctx) + (d.sym -> info) - case c: ClsLikeDefn => - createLiftInfoCls(c, ctx) + (d.sym -> info) - case _ => Map.empty - */ - - def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val defns = ctx.nestedDefns(f.sym) - defns.flatMap(createLiftInfoCont(_, N, ctx.addFnLocals(ctx.usedLocals(f.sym)))).toMap - - def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val defns = c.preCtor.gather(ctx) ++ c.ctor.gather(ctx) ++ c.companion.fold(Nil)(_.ctor.gather(ctx)) - val newCtx = if (c.companion.isDefined) && !ctx.ignored(c.sym) then ctx else ctx.addClsDefn(c) - val staticMtdInfo = c.companion.fold(Map.empty): - case value => value.methods.flatMap(f => createLiftInfoFn(f, newCtx)) - - defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap - ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) - ++ staticMtdInfo - // This rewrites code so that it's valid when lifted to the top level. // This way, no piece of code must be traversed by a BlockRewriter more than once. // Remark: This is why so much prior analysis is needed and is the main source of complexity in the lifter. @@ -497,17 +315,25 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version - case c @ Call(RefOfBms(l, S(d)), args) => ctx.rewrittenScopes.get(d) match - case None => super.applyResult(r)(k) - case Some(value) => value match - case f: LiftedFunc => k(f.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap)) - case ctor: RewrittenClassCtor => ctor.getRewrittenCls match - case cls: LiftedClass => - k(cls.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap)) - case _ => super.applyResult(r)(k) - case _ => super.applyResult(r)(k) + case c @ Call(RefOfBms(l, S(d)), args) => + val newCall = ctx.rewrittenScopes.get(d) match + case None => c + case Some(value) => value match + case f: LiftedFunc => f.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap) + case ctor: RewrittenClassCtor => ctor.getRewrittenCls match + case cls: LiftedClass => + cls.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap) + case _ => c + case _ => c + applyArgs(newCall.args): newArgs => + if (newCall.args is newArgs) && (c is newCall) then k(newCall) + else k(Call(newCall.fun, newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) case inst @ Instantiate(mut, InstSel(l, S(d)), args) => ctx.rewrittenScopes.get(d) match - case S(c: LiftedClass) => k(c.rewriteInstantiate(inst, ctx.capturesMap, ctx.symbolsMap)) + case S(c: LiftedClass) => + val newInst = c.rewriteInstantiate(inst, ctx.capturesMap, ctx.symbolsMap) + applyArgs(newInst.args): newArgs => + if (newInst.args is newArgs) && (newInst is inst) then k(newInst) + else k(Instantiate(newInst.mut, newInst.cls, newArgs)) case _ => super.applyResult(r)(k) case _ => super.applyResult(r)(k) @@ -605,15 +431,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): path.assign(rhs2, applySubBlock(rest)) case _ => super.applyBlock(rewritten) - // rewrite ValDefns (in ctors) - case define @ Define(d: ValDefn, rest: Block) if d.owner.isDefined => super.applyBlock(rewritten) // TODO - /* - ctx.getIsymPath(d.owner.get) match - case Some(value) if !iSymInScope(d.owner.get) => - applyResult(d.rhs): newRhs => - AssignField(value.read, Tree.Ident(d.sym.nme), newRhs, applyBlock(rest))(S(d.sym)) - case _ => super.applyBlock(rewritten) - */ // rewrite object definitions, assigning to the given symbol in modObjLocals case Define(d: ClsLikeDefn, rest: Block) => super.applyBlock(rewritten) // TODO /* @@ -637,19 +454,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): pre.rest(remaining) - override def applyPath(p: Path)(k: Path => Block): Block = - p match - // For objects inside classes: When an object is nested inside a class, its defn will be - // replaced by a symbol, to which the object instance is assigned. This rewrites references - // from the objects BlockMemberSymbol to that new symbol. - // case s @ Select(qual, ident) => ??? - /* - s.symbol.flatMap(ctx.getLocalPath) match - case Some(LocalPath.Sym(value: DefinitionSymbol[?])) => - k(Select(qual, Tree.Ident(value.nme))(S(value))) - case _ => super.applyPath(p)(k) - */ - + override def applyPath(p: Path)(k: Path => Block): Block = p match // This rewrites naked references to locals, case Value.Ref(l, _) => ctx.symbolsMap.get(l) match case Some(value) => k(value.read) @@ -657,172 +462,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case _ => super.applyPath(p)(k) - /* - * Explanation of liftOutDefnCont, liftDefnsInCls, liftDefnsInFn: - * - * The initial call is to liftDefnsInFn or liftDefnsInCls: - * - liftDefnsInFn rewrites a function's body so that it references variables correctly, and calls liftOutDefnCont - * on its nested definitions and lifts them (if they're not ignored). - * - liftDefnsInCls does the same but for classes by rewriting their constructors and methods. Notably, it directly - * calls liftDefnsInFn on its member functions. - * - * liftOutDefnCont's purpose is to rewrite definitions' signatures so that they make sense after being lifted. This - * includes adding the parameter lists which take in variables, captures, references to inner symbols etc. If a - * definition has been marked as "ignored" (not lifted), or if the definition is so simple that it doesn't need, - * extra parameter lists, it will directly call liftDefnsInFn or liftDefnsInCls on that definition. - */ - def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match - case N => d match - case f: FunDefn => ??? - case c: ClsLikeDefn => ??? - case _ => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms)) => - - def createSymbolsUpdateCtx[T <: LocalPath](createSym: String => (VarSymbol, T)) - : (List[Param], LifterCtx, List[(Local, (VarSymbol, T))]) - = - ??? - - d match - case f: FunDefn => - ??? - case c: ClsLikeDefn => - val fresh = FreshInt() - def createSym(nme: String): (VarSymbol, LocalPath.PubField) = - ( - VarSymbol(Tree.Ident(nme)), - LocalPath.PubField(c.isym, BlockMemberSymbol(nme, Nil, true)) - ) - val (extraParams, newCtx, flds) = createSymbolsUpdateCtx(createSym) - - // add aux params, private fields, update preCtor - val newAuxParams = c.auxParams.appended(PlainParamList(extraParams)) - - val pubFieldsPairs = flds.map: - case (_, (vs, LocalPath.PubField(isym, sym))) => vs -> sym - - val newPubFields = c.publicFields ::: pubFieldsPairs.map(_._2).map(bsym => bsym -> - TermSymbol(syntax.MutVal, S(c.isym), Tree.Ident(bsym.nme))) - - val newCtor = pubFieldsPairs.foldRight(c.ctor): - case ((sym, bms), blk) => Define(ValDefn.mk(S(c.isym), syntax.MutVal, bms, sym.asPath), blk) - - if modOrObj(c) then // module or object - // force it to be a class - val newK = c.k match - case syntax.Obj => syntax.Cls - case _ => wat("unreachable", c.k) - - val newDef = c.copy( - k = newK, paramsOpt = N, - owner = N, auxParams = PlainParamList(extraParams) :: Nil, - publicFields = newPubFields, - ctor = newCtor - ) - ??? - else // normal class - - val newDef = c.copy( - owner = N, - auxParams = newAuxParams, - publicFields = newPubFields, - ctor = newCtor - ) - - val Lifted(lifted, extras) = ??? - - val bms = fakeCtorBms.get - - // create the fake ctor here - inline def mapParams(ps: ParamList) = ps.params.map(p => VarSymbol(p.sym.id)) - - val paramSyms = c.paramsOpt.map(mapParams) // what is defined in paramsOpt - val auxSyms = c.auxParams.map(mapParams) // the original class's aux params - val extraSyms = extraParams.map(p => VarSymbol(p.sym.id)) // these will be added to the aux params - - // pop one list fromm auxSyms if paramsOpt is empty - // these are for creating the body only - val (newParamSyms, newAuxSyms) = paramSyms match - case None => auxSyms match - case head :: next => (S(head), next.appended(extraSyms)) - case Nil => (S(extraSyms), Nil) - case Some(value) => (paramSyms, auxSyms.appended(extraSyms)) - - val paramArgs = newParamSyms.getOrElse(Nil).map(_.asPath.asArg) - - inline def toPaths(l: List[Local]) = l.map(_.asPath) - - val isMutSym = VarSymbol(Tree.Ident("isMut")) - - val curSyms: MutSet[Local] = MutSet.empty - var curSym = TempSymbol(None, "tmp") - curSyms.add(curSym) - def instInner(isMut: Bool) = - Instantiate(mut = isMut, Value.Ref(c.sym, S(c.isym)), paramArgs) - - val initSym = curSym - - var acc: Block => Block = blk => Match( - isMutSym.asPath, - Case.Lit(Tree.BoolLit(true)) -> Assign(initSym, instInner(true), End()) :: Nil, - S(Assign(initSym, instInner(false), End())), - blk - ) - - for ps <- newAuxSyms do - val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false, false) - curSym = TempSymbol(None, "tmp") - curSyms.add(curSym) - val thisSym = curSym - acc = acc.assign(thisSym, call) - // acc = blk => acc(Assign(curSym, call, blk)) - val bod = Scoped(curSyms, acc.ret(curSym.asPath)) - - inline def toPlist(ls: List[VarSymbol]) = - PlainParamList(ls.map(s => Param(FldFlags.empty, s, N, Modulefulness.none))) - - val paramPlist = paramSyms.map(toPlist) - val auxPlist = auxSyms.map(toPlist) - // isMut determines whether the instantiation is `new` or `new mut` - val extraPlist = toPlist(isMutSym :: extraSyms) - - // NOTE: The fake ctor was to support first-class classes. - // These are currently unused. - - /* - val plist = paramPlist match - case None => extraPlist :: PlainParamList(Nil) :: auxPlist - case Some(value) => extraPlist :: value :: auxPlist - - val fakeCtorDefn = FunDefn( - None, bms, plist, bod - ) - */ - - val paramSym2 = paramSyms.getOrElse(Nil) - val auxSym2 = auxSyms.flatMap(l => l) - val allSymsMp = (paramSym2 ++ auxSym2 ++ extraSyms).map(s => s -> VarSymbol(s.id)).toMap - val subst = new SymbolSubst(): - override def mapVarSym(s: VarSymbol): VarSymbol = allSymsMp.get(s) match - case None => s - case Some(value) => value - - val (headParams, newAuxPlist) = paramPlist match - case None => auxPlist match - case head :: next => (ParamList(head.flags, extraPlist.params ++ head.params, head.restParam), next) - case Nil => (extraPlist, auxPlist) - - case Some(value) => (ParamList(value.flags, extraPlist.params ++ value.params, value.restParam), auxPlist) - - val auxCtorDefn_ = FunDefn(None, singleCallBms.b, singleCallBms.d, headParams :: newAuxPlist, bod)(false) - val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) - - // Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) - Lifted(lifted, extras ::: (auxCtorDefn :: Nil)) - case _ => Lifted(d, Nil) - - end liftOutDefnCont - given ignoredScopes: IgnoredScopes = IgnoredScopes(N) val data = ScopeData(topLevelBlk) val metadata = data.root.children.foldLeft(LifterMetadata.empty)(_ ++ createMetadata(_)) @@ -843,7 +482,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): println(v) println(")") - /* + println("accessesShallow") printMap(usedVars.shallowAccesses) @@ -853,7 +492,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): println("usedVars") printMap(usedVars.reqdCaptures) - */ + def isIgnored(d: Defn) = d match @@ -1188,11 +827,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewriterPreCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) - val preCtorWithCap = addCaptureSym(rewrittenPrector, captureSym, false) + val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( - ctor = rewrittenCtor, - preCtor = preCtorWithCap, + ctor = ctorWithCap, + preCtor = rewrittenPrector, privateFields = captureSym :: obj.cls.privateFields, methods = newMtds ) @@ -1363,14 +1002,15 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewriterPreCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) - val preCtorWithCap = addCaptureSym(rewrittenPrector, captureSym, false) + + val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) // Assign passed locals and captures - val preCtorWithPassed = passedSymsOrdered.foldRight(preCtorWithCap): + val ctorWithPassed = passedSymsOrdered.foldRight(ctorWithCap): case (sym, acc) => val (vs, ts) = passedSymsMap_(sym) Assign(ts, vs.asPath, acc) - val preCtorWithCaps = capturesOrdered.foldRight(preCtorWithPassed): + val ctorWithCaps = capturesOrdered.foldRight(ctorWithPassed): case (sym, acc) => val (vs, ts) = capSymsMap_(sym) Assign(ts, vs.asPath, acc) @@ -1390,8 +1030,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( owner = N, - ctor = rewrittenCtor, - preCtor = preCtorWithCaps, + ctor = ctorWithCaps, + preCtor = rewrittenPrector, privateFields = captureSym :: obj.cls.privateFields, methods = newMtds, paramsOpt = newPlist, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 5fe43f55e4..301ca7b969 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -77,13 +77,14 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes override def applyPath(p: Path): Unit = p match case Value.Ref(_: BuiltinSymbol, _) => super.applyPath(p) - case RefOfBms(_, SDSym(dSym)) if scopeData.contains(dSym) => + case RefOfBms(_, SDSym(dSym)) => // Check if it's referencing a class method. // If so, then it requires reading the class symbol val node = scopeData.getNode(dSym) node.obj match case Func(isMethod = false) => accessed.refdDefns.add(node.obj.toInfo) case f @ Func(isMethod = true) => accessed.accessed.add(f.fun.owner.get) + case _: ScopedObject.Class | _: ClassCtor => accessed.refdDefns.add(node.obj.toInfo) case _ => () case Value.Ref(l, _) => accessed.accessed.add(l) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 35f72c8f47..1e7d2b3a24 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -12,30 +12,265 @@ class Eff //│ Elab: { Cls Eff { }; } -:sjs +:effectHandlers :lift -fun f() = - fun g() = - let x = 1 - fun h() = - x - h - g() -//│ Elab: { fun member:f‹693›() = { fun member:g‹692›() = { let x‹696›; x‹696› = 1; fun member:h‹691›() = x‹696›#666; member:h‹691›#666 }; member:g‹692›#666() }; } -//│ JS (unsanitized): -//│ let h, g, f, h$; -//│ h$ = function h$(x) { -//│ return () => { -//│ return h(x) -//│ } -//│ }; -//│ h = function h(x) { -//│ return x -//│ }; -//│ g = function g() { -//│ let x, h$here; -//│ x = 1; -//│ h$here = h$(x); -//│ return h$here -//│ }; -//│ f = function f() { return g() }; +:noSanityCheck +:slot +handle h1 = Eff with + fun perform()(k) = + print("h1") + k() +handle h2 = Eff with + fun perform()(k) = + print("h2") + h1.perform() + k() +handle h3 = Eff with + fun perform()(k) = + print("h3") + h2.perform() + k() +h1.perform() +h3.perform() +//│ Elab: { handle h1‹694› = Ref(member:Eff‹686›)() List(HandlerTermDefinition(k‹699›,TermDefinition(Fun,member:perform‹698›,term:class:Handler$h1$‹695›.perform‹704›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h1)),None))))),App(Ref(k‹699›),Tup(List())))),‹result of member:perform‹698››‹703›,‹method ›,Modulefulness(None),List(),None))) in Handle(h2‹705›,Ref(member:Eff‹686›),List(),class:Handler$h2$‹706›,List(HandlerTermDefinition(k‹710›,TermDefinition(Fun,member:perform‹709›,term:class:Handler$h2$‹706›.perform‹716›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h2)),None)))), App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Ref(k‹710›),Tup(List())))),‹result of member:perform‹709››‹715›,‹method ›,Modulefulness(None),List(),None))),Handle(h3‹717›,Ref(member:Eff‹686›),List(),class:Handler$h3$‹718›,List(HandlerTermDefinition(k‹722›,TermDefinition(Fun,member:perform‹721›,term:class:Handler$h3$‹718›.perform‹728›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h3)),None)))), App(Sel(Ref(h2‹705›),Ident(perform)),Tup(List()))),App(Ref(k‹722›),Tup(List())))),‹result of member:perform‹721››‹727›,‹method ›,Modulefulness(None),List(),None))),Blk(List(App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Sel(Ref(h3‹717›),Ident(perform)),Tup(List()))))) } +//│ Pretty Lowered: +//│ +//│ define fun Handler$h2$perform(k) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc = 0 in +//│ end +//│ else +//│ set pc = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main = match pc +//│ 0 => +//│ set runtime.resumeValue = Predef.print("h2") in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$perform, 2, "HkScratch.mls:25:5", null, null, 1, 1, k, 0) +//│ in +//│ set pc = 2 in +//│ continue main +//│ 1 => +//│ set tmp = runtime.resumeValue in +//│ return k() +//│ 2 => +//│ set tmp1 = runtime.resumeValue in +//│ set runtime.resumeValue = ‹not in scope: h1‹694››.perform() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$perform, 1, "HkScratch.mls:26:5", null, null, 1, 1, k, 0) +//│ in +//│ set pc = 1 in +//│ continue main +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun Handler$h3$perform$(h2)(k1) { +//│ return Handler$h3$perform(h2, k1) +//│ } in +//│ define fun Handler$h3$perform(h21, k2) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc1 = 0 in +//│ end +//│ else +//│ set pc1 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main1 = match pc1 +//│ 0 => +//│ set runtime.resumeValue = Predef.print("h3") in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h3$perform, 2, "HkScratch.mls:30:5", null, null, 1, 2, h21, k2, 0) +//│ in +//│ set pc1 = 2 in +//│ continue main1 +//│ 1 => +//│ set tmp2 = runtime.resumeValue in +//│ return k2() +//│ 2 => +//│ set tmp3 = runtime.resumeValue in +//│ set runtime.resumeValue = h21.perform() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h3$perform, 1, "HkScratch.mls:31:7", null, null, 1, 2, h21, k2, 0) +//│ in +//│ set pc1 = 1 in +//│ continue main1 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun handleBlock$$(h3)() { +//│ return handleBlock$(h3) +//│ } in +//│ define class Handler$h3$ { +//│ let Handler$h3$$cap = ... +//│ } in +//│ define fun handleBlock$(h31) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc2 = 0 in +//│ end +//│ else +//│ set pc2 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main2 = match pc2 +//│ 0 => +//│ set runtime.resumeValue = ‹not in scope: h1‹694››.perform() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(handleBlock$, 1, "HkScratch.mls:33:1", null, null, 1, 1, h31, 0) +//│ in +//│ set pc2 = 1 in +//│ continue main2 +//│ 1 => +//│ set tmp4 = runtime.resumeValue in +//│ return h31.perform() +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun handleBlock$$(h23)() { +//│ return handleBlock$(h23) +//│ } in +//│ define class Handler$h2$ { +//│ let Handler$h2$$cap = ... +//│ } in +//│ define fun handleBlock$(h24) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc3 = 0 in +//│ end +//│ else +//│ set pc3 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main3 = match pc3 +//│ 0 => +//│ set h32 = new mut Handler$h3$(h24) in +//│ set handleBlock$$here = handleBlock$$(h32) in +//│ set runtime.resumeValue = runtime.enterHandleBlock(h32, handleBlock$$here) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 1, h24, 0) +//│ in +//│ set pc3 = 1 in +//│ continue main3 +//│ 1 => +//│ set tmp5 = runtime.resumeValue in +//│ return tmp5 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun Handler$h1$perform(k3) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc4 = 0 in +//│ end +//│ else +//│ set pc4 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main4 = match pc4 +//│ 0 => +//│ set runtime.resumeValue = Predef.print("h1") in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$perform, 1, "HkScratch.mls:21:5", null, null, 1, 1, k3, 0) +//│ in +//│ set pc4 = 1 in +//│ continue main4 +//│ 1 => +//│ set tmp6 = runtime.resumeValue in +//│ return k3() +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define class Handler$h1$ { +//│ let Handler$h1$$cap = ... +//│ } in +//│ set h1 = new mut Handler$h1$() in +//│ define fun handleBlock$() { +//│ match runtime.resumePc +//│ -1 => +//│ set pc5 = 0 in +//│ end +//│ else +//│ set pc5 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main5 = match pc5 +//│ 0 => +//│ set h25 = new mut Handler$h2$() in +//│ set handleBlock$$here1 = handleBlock$$(h25) in +//│ set runtime.resumeValue = runtime.enterHandleBlock(h25, handleBlock$$here1) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 0, 0) +//│ in +//│ set pc5 = 1 in +//│ continue main5 +//│ 1 => +//│ set tmp7 = runtime.resumeValue in +//│ return tmp7 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ set tmp8 = runtime.enterHandleBlock(h1, handleBlock$) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp8 = runtime.topLevelEffect(false) in +//│ end +//│ in +//│ set block$res2 = tmp8 in +//│ return undefined +//│ > h1 +//│ > h3 +//│ > h2 +//│ > h1 diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index efb7527ad5..d100ca8bce 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -12,48 +12,14 @@ class Eff //│ Elab: { Cls Eff { }; } -// function call and defn inside handler -:sjs :lift -fun f = - fun g = - let y = 2 - fun h() = set y += 1 - h -//│ Elab: { fun member:f‹693› = { fun member:g‹692› = { let y‹696›; y‹696› = 2; fun member:h‹691›() = y‹696›#666 := builtin:+‹48›#0(y‹696›#666, 1); member:h‹691›#666 }; () }; } -//│ JS (unsanitized): -//│ let h, g, f, g$, Capture$scope11, h$; -//│ (class Capture$scope1 { -//│ static { -//│ Capture$scope11 = this -//│ } -//│ constructor(y$0) { -//│ this.y$0 = y$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Capture$scope1"]; -//│ }); -//│ h$ = function h$(scope1$cap) { -//│ return () => { -//│ return h(scope1$cap) -//│ } -//│ }; -//│ h = function h(scope1$cap) { -//│ let tmp; -//│ tmp = scope1$cap.y$0 + 1; -//│ scope1$cap.y$0 = tmp; -//│ return runtime.Unit -//│ }; -//│ g$ = function g$() { -//│ return () => { -//│ return g() -//│ } -//│ }; -//│ g = function g() { -//│ let y, scope1$cap, h$here; -//│ scope1$cap = new Capture$scope11(y); -//│ scope1$cap.y$0 = 2; -//│ h$here = h$(scope1$cap); -//│ return h$here -//│ }; -//│ f = function f() { return runtime.Unit }; +fun f(x) = + class A with + fun getX = x + fun g = new A + g +//│ Elab: { fun member:f‹694›(x‹695›) = { Cls A { method fun member:getX‹691› = x‹695›#666; }; fun member:g‹693› = new member:A‹692›#666; member:g‹693›#666 }; } +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' +//│ ╟── which references the symbol introduced here +//│ ║ l.16: fun f(x) = +//│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 6572c17e73..d354f9c106 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -16,8 +16,7 @@ fun f(x) = a(1) f(2) //│ > 2 -//│ ═══[RUNTIME ERROR] Expected: '[1]', got: '[]' -//│ = [] +//│ = [1] // The following are problems with lifting classes inside other definitions. @@ -83,8 +82,8 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. -//│ = 1 +//│ ═══[RUNTIME ERROR] Expected: '1', got: '2' +//│ = 2 :todo @@ -100,14 +99,12 @@ fun foo1(x, n) = fun foo2() = class C() C -//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :w fun foo3() = class C() fun f = C() C -//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :todo :expect "NN" @@ -134,9 +131,7 @@ class A(a) with fun g() = x g() A(2).f() -//│ ═══[COMPILATION ERROR] Uses of private fields cannot yet be lifted. -//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 :expect 2 module M with @@ -145,8 +140,10 @@ module M with fun g() = x g M.f()() -//│ ═══[COMPILATION ERROR] Uses of private fields cannot yet be lifted. -//│ ═══[RUNTIME ERROR] Expected: '2', got: '()' +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.142: module M with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing /// The following are related to modules and objects. /// @@ -158,11 +155,29 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() -//│ ═══[WARNING] Modules are not yet lifted. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.155: module M with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 14 foo(10, 0) -//│ = 14 +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.164: foo(10, 0) +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.154: fun foo(x, y) = +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.155: module M with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.156: val test = 2 +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.157: fun foo() = +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.158: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' fun foo(x, y) = module M with @@ -171,12 +186,28 @@ fun foo(x, y) = x + y fun foo = M.foo() foo -//│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'M$' +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.168: module M with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 12 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: M$ is not defined +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.178: foo(10, 0) +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.167: fun foo(x, y) = +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.168: module M with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.169: fun foo() = +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.170: set y = 2 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.171: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -184,11 +215,27 @@ data class A(x) with module M with fun getB() = x fun getA() = M.getB() -//│ ═══[WARNING] Modules are not yet lifted. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.184: module M with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 2 A(2).getA() -//│ = 2 +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ║ l.190: A(2).getA() +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.183: data class A(x) with +//│ ║ ^^^^^^^^^ +//│ ║ l.184: module M with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.185: fun getB() = x +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.186: fun getA() = M.getB() +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ +//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' // TODO: Foo needs to be put in a mutable capture. Also, we need to pass the Foo instance itself into Foo fun foo(x) = @@ -202,8 +249,7 @@ foo(2) //│ ║ l.198: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'Foo$' -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'Foo$' +//│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null // `h` is lifted out, but then cannot access the BlockMemberSymbol M. @@ -215,7 +261,9 @@ fun f = fun h = M.g h M.g -//│ ═══[WARNING] Modules are not yet lifted. -//│ /!!!\ Uncaught error: hkmc2.InternalError: `this` not in scope: class:M +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.213: module M with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls index f753b520d8..928b0b2560 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls @@ -19,8 +19,13 @@ fun f(x) = let z = 3 fun g() = z //│ JS (unsanitized): -//│ let f, g$; +//│ let g, f, g$; //│ g$ = function g$(z) { +//│ return () => { +//│ return g(z) +//│ } +//│ }; +//│ g = function g(z) { //│ return z //│ }; //│ f = function f(x) { @@ -110,8 +115,13 @@ fun f(x) = let z = 3 fun g() = z ) //│ JS (unsanitized): -//│ let f4, g$3; +//│ let g5, f4, g$3; //│ g$3 = function g$(z) { +//│ return () => { +//│ return g5(z) +//│ } +//│ }; +//│ g5 = function g(z) { //│ return z //│ }; //│ f4 = function f(x) { diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index 297681f6c2..9b0039af5a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -57,7 +57,7 @@ fun f() = //│ k = 2000; //│ runtime.resumeValue = Predef.equals(i, 0); //│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(f, 5, "Debugging.mls:17:6", f$debugInfo, null, 1, 0, 6, i, j, k, scrut, tmp1, tmp2) +//│ return runtime.unwind(f, 5, "Debugging.mls:17:8", f$debugInfo, null, 1, 0, 6, i, j, k, scrut, tmp1, tmp2) //│ } //│ pc = 5; //│ continue main; @@ -118,7 +118,7 @@ lambda_test(() => 100) //│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect //│ at lambda (Debugging.mls:117:3) -//│ at lambda_test (Debugging.mls:114:3) +//│ at lambda_test (pc=1) import "../../mlscript-compile/Runtime.mls" @@ -157,6 +157,23 @@ module Test with j / i fun main()(using Debugger) = test(12) + test(34) +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.151: module Test with +//│ ╙── ^^^^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // Currently this test fails due to lifter issue. Commenting out :lift at the top of this file will make this work. // The lifter currently does not correctly consider the member variable as a local that could be captured. @@ -166,11 +183,21 @@ let res = fun break(payload)(resume) = resume using Debugger = h Runtime.try(() => Test.main()) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'instance$Ident(Debugger)' +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'Test' +//│ ║ l.185: Runtime.try(() => Test.main()) +//│ ║ ^^^^ //│ ╟── which references the symbol introduced here -//│ ║ l.167: using Debugger = h -//│ ╙── ^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: instance$Ident is not defined +//│ ║ l.151: module Test with +//│ ║ ^^^^^^^^^ +//│ ║ l.152: fun test(j)(using dbg: Debugger) = +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.153: let i = 0 +//│ ║ ^^^^^^^^^^^^^ +//│ ║ l.154: let k = 2000 +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.155: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ═══[RUNTIME ERROR] ReferenceError: Test is not defined //│ res = undefined :re @@ -205,8 +232,8 @@ fun f() = f() //│ > Stack Trace: -//│ > at f (Debugging.mls:199:3) with locals: j=200 +//│ > at f (Debugging.mls:226:3) with locals: j=200 //│ > Stack Trace: -//│ > at f (Debugging.mls:201:3) +//│ > at f (Debugging.mls:228:3) //│ > Stack Trace: //│ > at tail position diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls index 00853d2e4e..61fd0825ad 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectInHandler.mls @@ -24,7 +24,7 @@ h.aux(3) //│ ╔══[ERROR] Name not found: h //│ ║ l.19: h.p(x) //│ ╙── ^ -//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//│ FAILURE: Unexpected lack of runtime error handle h1 = PrintEffect with fun p(x)(k) = diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index a18551b989..985622037c 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -71,9 +71,13 @@ in h.perform("5") result //│ JS (unsanitized): -//│ let result, h7, tmp7, handleBlock$7, Handler$h$perform7, Handler$h$15, Handler$h$perform$7; -//│ result = ""; -//│ Handler$h$perform$7 = function Handler$h$perform$(Handler$h$$instance, arg, k) { +//│ let result, h7, tmp7, handleBlock$7, Handler$h$perform7, Handler$h$15, Handler$h$perform$4; +//│ Handler$h$perform$4 = function Handler$h$perform$(arg) { +//│ return (k) => { +//│ return Handler$h$perform7(arg, k) +//│ } +//│ }; +//│ Handler$h$perform7 = function Handler$h$perform(arg, k) { //│ let v, pc; //│ if (runtime.resumePc === -1) { //│ pc = 0; @@ -87,7 +91,7 @@ result //│ case 0: //│ runtime.resumeValue = runtime.safeCall(k(arg)); //│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h$perform$7, 1, "Effects.mls:63:13", null, null, 1, 3, Handler$h$$instance, arg, k, 0) +//│ return runtime.unwind(Handler$h$perform7, 1, null, null, null, 1, 2, arg, k, 0) //│ } //│ pc = 1; //│ continue main; @@ -101,11 +105,7 @@ result //│ break; //│ } //│ }; -//│ Handler$h$perform7 = function Handler$h$perform(Handler$h$$instance, arg) { -//│ return (k) => { -//│ return Handler$h$perform$7(Handler$h$$instance, arg, k) -//│ } -//│ }; +//│ result = ""; //│ (class Handler$h$14 extends Effect1 { //│ static { //│ Handler$h$15 = this @@ -118,9 +118,10 @@ result //│ } //│ tmp8; //│ } +//│ #Handler$h$$cap; //│ perform(arg) { //│ let Handler$h$perform$here; -//│ Handler$h$perform$here = runtime.safeCall(Handler$h$perform7(this, arg)); +//│ Handler$h$perform$here = Handler$h$perform$4(arg); //│ return runtime.mkEffect(this, Handler$h$perform$here) //│ } //│ toString() { return runtime.render(this); } @@ -276,11 +277,65 @@ module A with data class Effect(x) with fun test() = x fun perform(arg) +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.275: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) handle h = A.Effect(3) with fun perform()(k) = 0 h.perform() -//│ = 0 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res4, $tmp -> tmp2, member:handleBlock$ -> handleBlock$2, $runtime -> runtime, member:Handler$h$perform -> Handler$h$perform2, $definitionMetadata -> definitionMetadata, member:Handler$h$perform$ -> Handler$h$perform$7, $prettyPrint -> prettyPrint, $Term -> Term, member:Handler$h$ -> Handler$h$5, $Block -> Block, $Shape -> Shape, h -> h3, $block$res -> block$res10, class:Handler$h$ -> Handler$h$6, $tmp -> tmp7, member:handleBlock$ -> handleBlock$7, member:Handler$h$perform -> Handler$h$perform7, member:Handler$h$ -> Handler$h$15, $block$res -> block$res5, $tmp -> tmp3, member:handleBlock$ -> handleBlock$3, member:Handler$h$perform$ -> Handler$h$perform$4, member:Handler$h$perform -> Handler$h$perform3, member:Handler$h$ -> Handler$h$7, member:Handler$h$perform$ -> Handler$h$perform$, member:f -> f, h -> h11, class:Handler$h$ -> Handler$h$22, h -> h4, class:Handler$h$ -> Handler$h$8, h -> h8, $block$res -> block$res14, class:Handler$h$ -> Handler$h$16, $tmp -> tmp11, member:handleBlock$ -> handleBlock$11, member:Handler$h$perform -> Handler$h$perform11, member:Handler$h$ -> Handler$h$23, member:Handler$h$perform$ -> Handler$h$perform$8, $block$res -> block$res6, $tmp -> tmp4, member:handleBlock$ -> handleBlock$4, $block$res -> block$res11, member:Handler$h$perform -> Handler$h$perform4, $tmp -> tmp8, member:handleBlock$ -> handleBlock$8, member:Handler$h$ -> Handler$h$9, member:Handler$h$perform -> Handler$h$perform8, member:Handler$h$perform$ -> Handler$h$perform$1, member:Handler$h$ -> Handler$h$17, member:Handler$h$perform$ -> Handler$h$perform$5, member:foo -> foo, h -> h5, class:Handler$h$ -> Handler$h$10, member:foo -> foo2, h -> h12, h -> h9, class:Handler$h$ -> Handler$h$24, $block$res -> block$res7, class:Handler$h$ -> Handler$h$18, $tmp -> tmp5, member:handleBlock$ -> handleBlock$5, member:Handler$h$perform -> Handler$h$perform5, member:Handler$h$ -> Handler$h$11, $block$res -> block$res15, $tmp -> tmp12, member:handleBlock$ -> handleBlock$12, member:Handler$h$perform$ -> Handler$h$perform$2, member:Handler$h$perform -> Handler$h$perform12, member:Handler$h$ -> Handler$h$25, $block$res -> block$res12, $tmp -> tmp9, member:handleBlock$ -> handleBlock$9, member:Handler$h$perform -> Handler$h$perform9, member:foo -> foo1, member:Handler$h$ -> Handler$h$19, member:Handler$h$perform$ -> Handler$h$perform$6, $block$res -> block$res8, h -> h6, class:Handler$h$ -> Handler$h$12, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res9, $tmp -> tmp6, $block$res -> block$res1, member:lambda -> lambda, member:handleBlock$ -> handleBlock$6, h -> h, class:Handler$h$ -> Handler$h$, member:Handler$h$perform -> Handler$h$perform6, member:bar -> bar, member:foobar -> foobar, member:foo -> foo3, member:Handler$h$ -> Handler$h$13, member:Handler$h$perform$ -> Handler$h$perform$3, $block$res -> block$res2, $tmp -> tmp, member:handleBlock$ -> handleBlock$, member:Handler$h$perform -> Handler$h$perform, member:Handler$h$ -> Handler$h$1, member:Predef -> Predef, h -> h1, class:Handler$h$ -> Handler$h$2, h -> h10, class:Handler$h$ -> Handler$h$20, result -> result, h -> h7, class:Handler$h$ -> Handler$h$14, $block$res -> block$res3, $tmp -> tmp1, member:handleBlock$ -> handleBlock$1, member:Handler$h$perform -> Handler$h$perform1, member:Handler$h$ -> Handler$h$3, $block$res -> block$res13, h -> h2, $tmp -> tmp10, class:Handler$h$ -> Handler$h$4, member:handleBlock$ -> handleBlock$10, member:Handler$h$perform -> Handler$h$perform10, member:Handler$h$ -> Handler$h$21) +//│ curThis = S of S of class:Handler$h$ +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ║ l.280: handle h = A.Effect(3) with +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.275: module A with +//│ ║ ^^^^^^ +//│ ║ l.276: data class Effect(x) with +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.277: fun test() = x +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.278: fun perform(arg) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined +//│ at REPL53:1:282 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) fun f(perform) = handle h = Effect with @@ -441,11 +496,19 @@ fun foo(h) = handle h = Eff with fun perform()(k) = k(()) foo(h) -//│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.437: module A with +//│ ╙── ^ +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.433: module A with +//│ ╙── ^ +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.429: module A with +//│ ╙── ^ +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.425: module A with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing // Access superclass fields handle h1 = Object with diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls index 8723c6aec2..b19710c0a4 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls @@ -3,6 +3,23 @@ :lift module M +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.5: module M +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // * Notice that the two module definitions are lifted to the top of the block. :sjs @@ -14,8 +31,12 @@ fun foo(h): module M = else module A A -//│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.15: module A +//│ ╙── ^ +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.12: module A +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index 6c515939be..dadfc26c54 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -32,6 +32,7 @@ data class Lol(h) with //│ } //│ tmp1; //│ } +//│ #Lol$cap; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Lol", ["h"]]; //│ }); @@ -117,6 +118,23 @@ fun f() = () module A with f() () // defeats tail call optimization +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.117: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) let obj = new Object with fun foo() = print("Hello") diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls index 67b143ed20..5278c85c00 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls @@ -5,4 +5,94 @@ abstract class Effect +class PrintEffect +:sjs +// :sjs +handle h = PrintEffect with + fun p(x)(k) = + print(x) + k(()) + fun aux(x)(k) = + h.p(x) + k(()) + h.p(x) +h.aux(3) +//│ FAILURE: Unexpected type error +//│ FAILURE LOCATION: subterm (Elaborator.scala:481) +//│ ╔══[ERROR] Name not found: h +//│ ║ l.17: h.p(x) +//│ ╙── ^ +//│ FAILURE: Unexpected type error +//│ FAILURE LOCATION: subterm (Elaborator.scala:481) +//│ ╔══[ERROR] Name not found: h +//│ ║ l.19: h.p(x) +//│ ╙── ^ +//│ JS (unsanitized): +//│ let h, tmp, handleBlock$, Handler$h$p, Handler$h$aux, Handler$h$1, Handler$h$p$; +//│ Handler$h$aux = function Handler$h$aux(k) { +//│ /* error */ +//│ }; +//│ Handler$h$p$ = function Handler$h$p$(x) { +//│ return (k) => { +//│ return Handler$h$p(x, k) +//│ } +//│ }; +//│ Handler$h$p = function Handler$h$p(x, k) { +//│ let tmp1, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ runtime.resumeValue = Predef.print(x); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h$p, 1, "HandlersScratch.mls:14:5", null, null, 1, 2, x, k, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp1 = runtime.resumeValue; +//│ return runtime.safeCall(k(runtime.Unit)); +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ (class Handler$h$ extends PrintEffect1 { +//│ static { +//│ Handler$h$1 = this +//│ } +//│ constructor() { +//│ let tmp1; +//│ tmp1 = super(); +//│ if (runtime.curEffect !== null) { +//│ tmp1 = runtime.illegalEffect("in a constructor"); +//│ } +//│ tmp1; +//│ } +//│ #Handler$h$$cap; +//│ p(x) { +//│ let Handler$h$p$here; +//│ Handler$h$p$here = Handler$h$p$(x); +//│ return runtime.mkEffect(this, Handler$h$p$here) +//│ } +//│ aux(x) { +//│ return runtime.mkEffect(this, Handler$h$aux) +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Handler$h$"]; +//│ }); +//│ h = new Handler$h$1(); +//│ handleBlock$ = (undefined, function () { +//│ return runtime.safeCall(h.aux(3)) +//│ }); +//│ tmp = runtime.enterHandleBlock(h, handleBlock$); +//│ if (runtime.curEffect !== null) { tmp = runtime.topLevelEffect(false); } +//│ tmp diff --git a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls index de1b403d98..a05382e0cd 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls @@ -8,7 +8,7 @@ // * It is notably interesting in that it demonstrates the ability to preserve tail calls. // * The original function can be found in `hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls` - +:sjs fun test(n, stackLimit) = let stackDepth = 0 let stackOffset = 0 @@ -62,6 +62,526 @@ fun test(n, stackLimit) = let ans = fact(n) set stackDepth = 0 ans +//│ JS (unsanitized): +//│ let b, a, b1, a1, mkrec, selfApp, StackDelay1, test, handleBlock$, Handler$h$perform, Handler$h$1, Capture$scope01, handleBlock$$, a$, mkrec$, selfApp$, b$, a$1, b$1, Handler$h$perform$; +//│ (class Capture$scope0 { +//│ static { +//│ Capture$scope01 = this +//│ } +//│ constructor(stackDepth$0, stackOffset$1) { +//│ this.stackOffset$1 = stackOffset$1; +//│ this.stackDepth$0 = stackDepth$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$scope0"]; +//│ }); +//│ Handler$h$perform$ = function Handler$h$perform$(scope0$cap) { +//│ return (resume) => { +//│ return Handler$h$perform(scope0$cap, resume) +//│ } +//│ }; +//│ Handler$h$perform = function Handler$h$perform(scope0$cap, resume) { +//│ let curOffset, tmp, tmp1, tmp2, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ saveOffset = runtime.resumeIdx; +//│ curOffset = runtime.resumeArr.at(saveOffset); +//│ saveOffset = saveOffset + 1; +//│ tmp = runtime.resumeArr.at(saveOffset); +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ curOffset = scope0$cap.stackOffset$1; +//│ scope0$cap.stackOffset$1 = scope0$cap.stackDepth$0; +//│ runtime.resumeValue = globalThis.console.log("resuming at offset:", curOffset); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h$perform, 3, "ManualStackSafety.mls:21:7", null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) +//│ } +//│ pc = 3; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp2 = runtime.resumeValue; +//│ scope0$cap.stackOffset$1 = curOffset; +//│ return tmp; +//│ break; +//│ case 2: +//│ tmp = runtime.resumeValue; +//│ runtime.resumeValue = globalThis.console.log("finished at offset:", curOffset); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h$perform, 1, "ManualStackSafety.mls:23:7", null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 3: +//│ tmp1 = runtime.resumeValue; +//│ runtime.resumeValue = runtime.safeCall(resume()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h$perform, 2, null, null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) +//│ } +//│ pc = 2; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ selfApp$ = function selfApp$(scope0$cap, stackLimit, h) { +//│ return (f) => { +//│ return selfApp(scope0$cap, stackLimit, h, f) +//│ } +//│ }; +//│ b$1 = function b$(scope0$cap, stackLimit, h, self) { +//│ return (y) => { +//│ return b(scope0$cap, stackLimit, h, self, y) +//│ } +//│ }; +//│ b = function b(scope0$cap, stackLimit, h, self, y) { +//│ let scrut, tmp, res, tmp1, tmp2, tmp3, tmp4, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ saveOffset = runtime.resumeIdx; +//│ tmp = runtime.resumeArr.at(saveOffset); +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp1 = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp1 >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b, 3, "ManualStackSafety.mls:35:55", null, null, 1, 5, scope0$cap, stackLimit, h, self, y, 1, tmp) +//│ } +//│ pc = 3; +//│ continue main +//│ } else { +//│ tmp2 = runtime.Unit; +//│ } +//│ pc = 2; +//│ continue main; +//│ break; +//│ case 1: +//│ res = runtime.resumeValue; +//│ scope0$cap.stackDepth$0 = tmp; +//│ tmp4 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp4; +//│ return runtime.safeCall(res(y)); +//│ break; +//│ case 2: +//│ tmp = scope0$cap.stackDepth$0; +//│ tmp3 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp3; +//│ runtime.resumeValue = selfApp(scope0$cap, stackLimit, h, self); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b, 1, null, null, null, 1, 5, scope0$cap, stackLimit, h, self, y, 1, tmp) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 3: +//│ tmp2 = runtime.resumeValue; +//│ pc = 2; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ a$1 = function a$(scope0$cap, stackLimit, h, g) { +//│ return (self) => { +//│ return a(scope0$cap, stackLimit, h, g, self) +//│ } +//│ }; +//│ a = function a(scope0$cap, stackLimit, h, g, self) { +//│ let scrut, tmp, tmp1, tmp2, b$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(a, 2, "ManualStackSafety.mls:33:53", null, null, 1, 5, scope0$cap, stackLimit, h, g, self, 0) +//│ } +//│ pc = 2; +//│ continue main +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp2 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp2; +//│ b$here = b$1(scope0$cap, stackLimit, h, self); +//│ return runtime.safeCall(g(b$here)); +//│ break; +//│ case 2: +//│ tmp1 = runtime.resumeValue; +//│ pc = 1; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ mkrec$ = function mkrec$(scope0$cap, stackLimit, h) { +//│ return (g) => { +//│ return mkrec(scope0$cap, stackLimit, h, g) +//│ } +//│ }; +//│ b$ = function b$(scope0$cap, stackLimit, h, self) { +//│ return (x) => { +//│ return b1(scope0$cap, stackLimit, h, self, x) +//│ } +//│ }; +//│ b1 = function b(scope0$cap, stackLimit, h, self, x) { +//│ let scrut, scrut1, tmp, prev, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ saveOffset = runtime.resumeIdx; +//│ tmp = runtime.resumeArr.at(saveOffset); +//│ saveOffset = saveOffset + 1; +//│ prev = runtime.resumeArr.at(saveOffset); +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp1 = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp1 >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b1, 7, "ManualStackSafety.mls:50:55", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) +//│ } +//│ pc = 7; +//│ continue main +//│ } else { +//│ tmp2 = runtime.Unit; +//│ } +//│ pc = 6; +//│ continue main; +//│ break; +//│ case 5: +//│ scrut1 = runtime.resumeValue; +//│ if (scrut1 === true) { +//│ return 1 +//│ } else { +//│ runtime.resumeValue = globalThis.console.log(scope0$cap.stackDepth$0, scope0$cap.stackOffset$1); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b1, 4, "ManualStackSafety.mls:52:11", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) +//│ } +//│ pc = 4; +//│ continue main +//│ } +//│ break; +//│ case 1: +//│ tmp6 = runtime.resumeValue; +//│ return x * prev; +//│ break; +//│ case 6: +//│ runtime.resumeValue = Predef.equals(x, 0); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b1, 5, "ManualStackSafety.mls:51:14", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) +//│ } +//│ pc = 5; +//│ continue main; +//│ break; +//│ case 2: +//│ prev = runtime.resumeValue; +//│ scope0$cap.stackDepth$0 = tmp; +//│ runtime.resumeValue = globalThis.console.log("resumed:", x); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b1, 1, "ManualStackSafety.mls:57:11", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 7: +//│ tmp2 = runtime.resumeValue; +//│ pc = 6; +//│ continue main; +//│ break; +//│ case 3: +//│ runtime.resumeValue = runtime.safeCall(self(tmp5)); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(b1, 2, null, null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) +//│ } +//│ pc = 2; +//│ continue main; +//│ break; +//│ case 4: +//│ tmp3 = runtime.resumeValue; +//│ tmp = scope0$cap.stackDepth$0; +//│ tmp4 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp4; +//│ tmp5 = x - 1; +//│ pc = 3; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ a$ = function a$(scope0$cap, stackLimit, h) { +//│ return (self) => { +//│ return a1(scope0$cap, stackLimit, h, self) +//│ } +//│ }; +//│ selfApp = function selfApp(scope0$cap, stackLimit, h, f) { +//│ let scrut, tmp, tmp1, tmp2, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(selfApp, 2, "ManualStackSafety.mls:27:51", null, null, 1, 4, scope0$cap, stackLimit, h, f, 0) +//│ } +//│ pc = 2; +//│ continue main +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp2 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp2; +//│ return runtime.safeCall(f(f)); +//│ break; +//│ case 2: +//│ tmp1 = runtime.resumeValue; +//│ pc = 1; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ mkrec = function mkrec(scope0$cap, stackLimit, h, g) { +//│ let scrut, tmp, tmp1, tmp2, a$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(mkrec, 2, "ManualStackSafety.mls:31:51", null, null, 1, 4, scope0$cap, stackLimit, h, g, 0) +//│ } +//│ pc = 2; +//│ continue main +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp2 = scope0$cap.stackDepth$0 + 1; +//│ scope0$cap.stackDepth$0 = tmp2; +//│ a$here = a$1(scope0$cap, stackLimit, h, g); +//│ return selfApp(scope0$cap, stackLimit, h, a$here); +//│ break; +//│ case 2: +//│ tmp1 = runtime.resumeValue; +//│ pc = 1; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ a1 = function a(scope0$cap, stackLimit, h, self) { +//│ let scrut, tmp, tmp1, b$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; +//│ scrut = tmp >= stackLimit; +//│ if (scrut === true) { +//│ runtime.resumeValue = runtime.safeCall(h.perform()); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(a1, 2, "ManualStackSafety.mls:48:53", null, null, 1, 4, scope0$cap, stackLimit, h, self, 0) +//│ } +//│ pc = 2; +//│ continue main +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ b$here = b$(scope0$cap, stackLimit, h, self); +//│ return b$here; +//│ break; +//│ case 2: +//│ tmp1 = runtime.resumeValue; +//│ pc = 1; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }; +//│ handleBlock$$ = (undefined, function (scope0$cap, n, stackLimit, h) { +//│ return () => { +//│ return handleBlock$(scope0$cap, n, stackLimit, h) +//│ } +//│ }); +//│ (class StackDelay { +//│ static { +//│ StackDelay1 = this +//│ } +//│ constructor() {} +//│ #StackDelay$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "StackDelay"]; +//│ }); +//│ (class Handler$h$ extends StackDelay1 { +//│ static { +//│ Handler$h$1 = this +//│ } +//│ constructor(scope0$cap) { +//│ let tmp; +//│ tmp = super(); +//│ if (runtime.curEffect !== null) { +//│ tmp = runtime.illegalEffect("in a constructor"); +//│ } +//│ tmp; +//│ this.scope0$cap = scope0$cap; +//│ } +//│ #Handler$h$$cap; +//│ perform() { +//│ let Handler$h$perform$here; +//│ Handler$h$perform$here = Handler$h$perform$(this.scope0$cap); +//│ return runtime.mkEffect(this, Handler$h$perform$here) +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Handler$h$"]; +//│ }); +//│ handleBlock$ = (undefined, function (scope0$cap, n, stackLimit, h) { +//│ let fact, ans, a$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ a$here = a$(scope0$cap, stackLimit, h); +//│ runtime.resumeValue = mkrec(scope0$cap, stackLimit, h, a$here); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(handleBlock$, 2, null, null, null, 1, 4, scope0$cap, n, stackLimit, h, 0) +//│ } +//│ pc = 2; +//│ continue main; +//│ break; +//│ case 1: +//│ ans = runtime.resumeValue; +//│ scope0$cap.stackDepth$0 = 0; +//│ return ans; +//│ break; +//│ case 2: +//│ fact = runtime.resumeValue; +//│ scope0$cap.stackDepth$0 = 1; +//│ runtime.resumeValue = runtime.safeCall(fact(n)); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 4, scope0$cap, n, stackLimit, h, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ } +//│ break; +//│ } +//│ }); +//│ test = function test(n, stackLimit) { +//│ let stackDepth, stackOffset, h, tmp, scope0$cap, handleBlock$$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ scope0$cap = new Capture$scope01(stackDepth, stackOffset); +//│ scope0$cap.stackDepth$0 = 0; +//│ scope0$cap.stackOffset$1 = 0; +//│ h = new Handler$h$1(scope0$cap); +//│ handleBlock$$here = handleBlock$$(scope0$cap, n, stackLimit, h); +//│ runtime.resumeValue = runtime.enterHandleBlock(h, handleBlock$$here); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(test, 1, null, null, null, 1, 2, n, stackLimit, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp = runtime.resumeValue; +//│ return tmp; +//│ break; +//│ } +//│ break; +//│ } +//│ }; :expect 3628800 test(10, 100) diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index c7afa3ca81..0980562b45 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -2,17 +2,28 @@ :effectHandlers :lift - fun f() = (() => return 100)() print("Bad") f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. //│ = 100 fun foo(x) = let xs = [() => return x + 1] xs.0() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. foo(123) //│ = 124 @@ -31,6 +42,18 @@ fun f() = print("Bad") 100 f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. //│ > Ok //│ = 123 @@ -47,6 +70,18 @@ fun f() = print("Continue") 100 f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. //│ > Ok //│ > 200 //│ > Continue @@ -56,34 +91,46 @@ f() :re fun f() = () => return 3 f()() -//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_12 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_6 :re fun f(): () -> Int = () => return () => 3 f()() -//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_14 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_7 :e return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.68: return 100 -//│ ╙── ^^^^^^^^^^ +//│ ║ l.115: return 100 +//│ ╙── ^^^^^^^^^^ :e if true do while false do let f = () => return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.76: let f = () => return 100 -//│ ╙── ^^^^^^^^^^ +//│ ║ l.123: let f = () => return 100 +//│ ╙── ^^^^^^^^^^ :e fun f() = type A = return "lol" //│ ╔══[ERROR] Return statements are not allowed in this context. -//│ ║ l.83: type A = return "lol" -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.130: type A = return "lol" +//│ ╙── ^^^^^^^^^^^^ :expect 100 @@ -98,6 +145,12 @@ fun f() = f() print("Bad") f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. //│ = 100 // broken due to handlers currently not handling `object` properly @@ -108,8 +161,9 @@ fun f() = return 100 4 f() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'nonLocalRetHandler$f' -//│ ═══[RUNTIME ERROR] ReferenceError: nonLocalRetHandler$f is not defined +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'ret') //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' // ctor cannot raise effect, so error is expected. @@ -120,7 +174,9 @@ fun f() = return 100 new A f() -//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_20 is raised in a constructor +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_10 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme @@ -130,8 +186,9 @@ fun f() = (() => return 100)() 4 f() -//│ ═══[COMPILATION ERROR] No definition found in scope for member 'nonLocalRetHandler$f' -//│ ═══[RUNTIME ERROR] ReferenceError: nonLocalRetHandler$f is not defined +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'ret') //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme @@ -142,5 +199,7 @@ fun f() = new A 4 f() -//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_24 is raised in a constructor +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_12 is raised in a constructor //│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 4fdeb1a1af..d37fafb8a8 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -155,187 +155,188 @@ if true do h1.perform(()) str //│ JS (unsanitized): -//│ let str, scrut, h11, tmp11, tmp12; -//│ str = ""; -//│ scrut = true; -//│ if (scrut === true) { -//│ let handleBlock$9, Handler$h2$perform1, Handler$h2$3, handleBlock$10, Handler$h1$perform1, Handler$h1$3, Handler$h1$perform$1, handleBlock$$2, Handler$h2$perform$1; -//│ Handler$h1$perform$1 = function Handler$h1$perform$(Handler$h1$$instance, arg, k) { -//│ let tmp13, tmp14, tmp15, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; +//│ let str, scrut, h11, tmp11, tmp12, handleBlock$9, Handler$h2$perform1, Handler$h2$3, handleBlock$10, Handler$h1$perform1, Handler$h1$3, handleBlock$$2, Handler$h2$perform$1, Handler$h1$perform$1; +//│ Handler$h1$perform$1 = function Handler$h1$perform$(arg) { +//│ return (k) => { +//│ return Handler$h1$perform1(arg, k) +//│ } +//│ }; +//│ Handler$h1$perform1 = function Handler$h1$perform(arg, k) { +//│ let tmp13, tmp14, tmp15, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp13 = str + "A"; +//│ str = tmp13; +//│ runtime.resumeValue = runtime.safeCall(k(arg)); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h1$perform1, 1, null, null, null, 1, 2, arg, k, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp14 = runtime.resumeValue; +//│ tmp15 = str + "A"; +//│ str = tmp15; +//│ return runtime.Unit; +//│ break; //│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp13 = str + "A"; -//│ str = tmp13; -//│ runtime.resumeValue = runtime.safeCall(k(arg)); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h1$perform$1, 1, "RecursiveHandlers.mls:147:7", null, null, 1, 3, Handler$h1$$instance, arg, k, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp14 = runtime.resumeValue; -//│ tmp15 = str + "A"; -//│ str = tmp15; -//│ return runtime.Unit; -//│ break; -//│ } -//│ break; +//│ break; +//│ } +//│ }; +//│ Handler$h2$perform$1 = function Handler$h2$perform$(arg) { +//│ return (k) => { +//│ return Handler$h2$perform1(arg, k) +//│ } +//│ }; +//│ Handler$h2$perform1 = function Handler$h2$perform(arg, k) { +//│ let tmp13, tmp14, tmp15, tmp16, tmp17, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ tmp13 = str + "B"; +//│ tmp14 = str + tmp13; +//│ str = tmp14; +//│ runtime.resumeValue = runtime.safeCall(k(arg)); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(Handler$h2$perform1, 1, null, null, null, 1, 2, arg, k, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp15 = runtime.resumeValue; +//│ tmp16 = str + "B"; +//│ tmp17 = str + tmp16; +//│ str = tmp17; +//│ return runtime.Unit; +//│ break; //│ } -//│ }; -//│ Handler$h1$perform1 = function Handler$h1$perform(Handler$h1$$instance, arg) { -//│ return (k) => { -//│ return Handler$h1$perform$1(Handler$h1$$instance, arg, k) +//│ break; +//│ } +//│ }; +//│ handleBlock$$2 = (undefined, function (h22) { +//│ return () => { +//│ return handleBlock$9(h22) +//│ } +//│ }); +//│ (class Handler$h2$2 extends Effect1 { +//│ static { +//│ Handler$h2$3 = this +//│ } +//│ constructor() { +//│ let tmp13; +//│ tmp13 = super(); +//│ if (runtime.curEffect !== null) { +//│ tmp13 = runtime.illegalEffect("in a constructor"); //│ } -//│ }; -//│ (class Handler$h1$2 extends Effect1 { -//│ static { -//│ Handler$h1$3 = this +//│ tmp13; +//│ } +//│ #Handler$h2$$cap; +//│ perform(arg) { +//│ let Handler$h2$perform$here; +//│ Handler$h2$perform$here = Handler$h2$perform$1(arg); +//│ return runtime.mkEffect(this, Handler$h2$perform$here) +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Handler$h2$"]; +//│ }); +//│ handleBlock$9 = (undefined, function (h22) { +//│ let tmp13, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ runtime.resumeValue = runtime.safeCall(h22.perform(runtime.Unit)); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:154:5", null, null, 1, 1, h22, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp13 = runtime.resumeValue; +//│ return runtime.safeCall(h11.perform(runtime.Unit)); +//│ break; //│ } -//│ constructor() { -//│ let tmp13; -//│ tmp13 = super(); -//│ if (runtime.curEffect !== null) { -//│ tmp13 = runtime.illegalEffect("in a constructor"); -//│ } -//│ tmp13; +//│ break; +//│ } +//│ }); +//│ (class Handler$h1$2 extends Effect1 { +//│ static { +//│ Handler$h1$3 = this +//│ } +//│ constructor() { +//│ let tmp13; +//│ tmp13 = super(); +//│ if (runtime.curEffect !== null) { +//│ tmp13 = runtime.illegalEffect("in a constructor"); //│ } -//│ perform(arg) { -//│ let Handler$h1$perform$here; -//│ Handler$h1$perform$here = runtime.safeCall(Handler$h1$perform1(this, arg)); -//│ return runtime.mkEffect(this, Handler$h1$perform$here) +//│ tmp13; +//│ } +//│ #Handler$h1$$cap; +//│ perform(arg) { +//│ let Handler$h1$perform$here; +//│ Handler$h1$perform$here = Handler$h1$perform$1(arg); +//│ return runtime.mkEffect(this, Handler$h1$perform$here) +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Handler$h1$"]; +//│ }); +//│ handleBlock$10 = (undefined, function () { +//│ let h22, tmp13, handleBlock$$here, pc; +//│ if (runtime.resumePc === -1) { +//│ pc = 0; +//│ } else { +//│ let saveOffset; +//│ pc = runtime.resumePc; +//│ runtime.resumePc = -1; +//│ } +//│ main: while (true) { +//│ switch (pc) { +//│ case 0: +//│ h22 = new Handler$h2$3(); +//│ handleBlock$$here = handleBlock$$2(h22); +//│ runtime.resumeValue = runtime.enterHandleBlock(h22, handleBlock$$here); +//│ if (runtime.curEffect !== null) { +//│ return runtime.unwind(handleBlock$10, 1, null, null, null, 1, 0, 0) +//│ } +//│ pc = 1; +//│ continue main; +//│ break; +//│ case 1: +//│ tmp13 = runtime.resumeValue; +//│ return tmp13; +//│ break; //│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Handler$h1$"]; -//│ }); +//│ break; +//│ } +//│ }); +//│ str = ""; +//│ scrut = true; +//│ if (scrut === true) { //│ h11 = new Handler$h1$3(); -//│ Handler$h2$perform$1 = function Handler$h2$perform$(Handler$h2$$instance, arg, k) { -//│ let tmp13, tmp14, tmp15, tmp16, tmp17, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp13 = str + "B"; -//│ tmp14 = str + tmp13; -//│ str = tmp14; -//│ runtime.resumeValue = runtime.safeCall(k(arg)); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h2$perform$1, 1, "RecursiveHandlers.mls:152:7", null, null, 1, 3, Handler$h2$$instance, arg, k, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp15 = runtime.resumeValue; -//│ tmp16 = str + "B"; -//│ tmp17 = str + tmp16; -//│ str = tmp17; -//│ return runtime.Unit; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ Handler$h2$perform1 = function Handler$h2$perform(Handler$h2$$instance, arg) { -//│ return (k) => { -//│ return Handler$h2$perform$1(Handler$h2$$instance, arg, k) -//│ } -//│ }; -//│ (class Handler$h2$2 extends Effect1 { -//│ static { -//│ Handler$h2$3 = this -//│ } -//│ constructor() { -//│ let tmp13; -//│ tmp13 = super(); -//│ if (runtime.curEffect !== null) { -//│ tmp13 = runtime.illegalEffect("in a constructor"); -//│ } -//│ tmp13; -//│ } -//│ perform(arg) { -//│ let Handler$h2$perform$here; -//│ Handler$h2$perform$here = runtime.safeCall(Handler$h2$perform1(this, arg)); -//│ return runtime.mkEffect(this, Handler$h2$perform$here) -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Handler$h2$"]; -//│ }); -//│ handleBlock$$2 = function handleBlock$$(h22) { -//│ let tmp13, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ runtime.resumeValue = runtime.safeCall(h22.perform(runtime.Unit)); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$$2, 1, "RecursiveHandlers.mls:154:5", null, null, 1, 1, h22, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp13 = runtime.resumeValue; -//│ return runtime.safeCall(h11.perform(runtime.Unit)); -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ handleBlock$9 = (undefined, function (h22) { -//│ return () => { -//│ return handleBlock$$2(h22) -//│ } -//│ }); -//│ handleBlock$10 = (undefined, function () { -//│ let h22, tmp13, handleBlock$$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ h22 = new Handler$h2$3(); -//│ handleBlock$$here = runtime.safeCall(handleBlock$9(h22)); -//│ runtime.resumeValue = runtime.enterHandleBlock(h22, handleBlock$$here); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$10, 1, null, null, null, 1, 0, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp13 = runtime.resumeValue; -//│ return tmp13; -//│ break; -//│ } -//│ break; -//│ } -//│ }); //│ tmp11 = runtime.enterHandleBlock(h11, handleBlock$10); //│ if (runtime.curEffect !== null) { //│ tmp11 = runtime.topLevelEffect(false); diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 8dd274d2bf..8e3e020a25 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -16,7 +16,21 @@ fun f() = h.foo() return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() -//│ = 3 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) fun f() = handle h = Effect with @@ -28,10 +42,64 @@ fun f() = h.foo() return 3 10 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :expect 3 f() -//│ = 3 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:f +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res2, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res1, member:Predef -> Predef) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' +//│ ║ l.62: f() +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.35: fun f() = +//│ ║ ^^^^^^^^^ +//│ ║ l.36: handle h = Effect with +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.37: fun foo()(r) = +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.38: let m = r() +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.39: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: f is not defined +//│ at REPL13:1:29 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' :e let l = () => @@ -40,8 +108,8 @@ let l = () => return 3 4 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.40: return 3 -//│ ╙── ^^^^^^^^ +//│ ║ l.108: return 3 +//│ ╙── ^^^^^^^^ //│ l = fun l // :re @@ -58,7 +126,7 @@ let l = () => 4 l() //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.57: return 3 -//│ ╙── ^^^^^^^^ +//│ ║ l.125: return 3 +//│ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 85108f1869..9fa109a3e0 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -175,7 +175,23 @@ module A with else n + f(n-1) val x = f(10000) A.x -//│ = 50005000 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.172: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :re fun foo(h) = h.perform(2) diff --git a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls index c0290957d2..8681ec7f28 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsSafe.mls @@ -44,11 +44,11 @@ in //│ > main end //│ > f 2 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ -//│ at f (UserThreadsSafe.mls:18:3) -//│ at while$ (UserThreadsSafe.mls:11:7) +//│ at f (UserThreadsSafe.mls:18:4) +//│ at while (pc=1) //│ at ThreadEffect#drain (pc=1) -//│ at Handler$h$fork$ (UserThreadsSafe.mls:36:5) -//│ at Handler$h$fork$ (UserThreadsSafe.mls:36:5) +//│ at Handler$h$fork (pc=1) +//│ at Handler$h$fork (pc=1) // FIFO @@ -67,10 +67,10 @@ in //│ > main end //│ > f 0 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$2 -//│ at f (UserThreadsSafe.mls:18:3) -//│ at while$ (UserThreadsSafe.mls:11:7) +//│ at f (UserThreadsSafe.mls:18:4) +//│ at while (pc=1) //│ at ThreadEffect#drain (pc=1) -//│ at Handler$h$fork$ (UserThreadsSafe.mls:59:5) -//│ at Handler$h$fork$ (UserThreadsSafe.mls:59:5) +//│ at Handler$h$fork (pc=1) +//│ at Handler$h$fork (pc=1) diff --git a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls index 2b3e28bb4c..4abf8b990a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/UserThreadsUnsafe.mls @@ -47,11 +47,11 @@ in //│ > main end //│ > f 2 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ -//│ at f (UserThreadsUnsafe.mls:13:3) -//│ at while$ (UserThreadsUnsafe.mls:30:5) +//│ at f (UserThreadsUnsafe.mls:13:4) +//│ at while (pc=1) //│ at drain (pc=1) -//│ at Handler$h$fork$ (UserThreadsUnsafe.mls:39:5) -//│ at Handler$h$fork$ (UserThreadsUnsafe.mls:39:5) +//│ at Handler$h$fork (pc=1) +//│ at Handler$h$fork (pc=1) // FIFO @@ -70,10 +70,10 @@ in //│ > main end //│ > f 1 //│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ -//│ at f (UserThreadsUnsafe.mls:13:3) -//│ at while$ (UserThreadsUnsafe.mls:30:5) +//│ at f (UserThreadsUnsafe.mls:13:4) +//│ at while (pc=1) //│ at drain (pc=1) -//│ at Handler$h$fork$ (UserThreadsUnsafe.mls:62:5) -//│ at Handler$h$fork$ (UserThreadsUnsafe.mls:62:5) +//│ at Handler$h$fork (pc=1) +//│ at Handler$h$fork (pc=1) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls index 71e407799d..15d7344330 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls @@ -15,14 +15,14 @@ fun foo(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) //│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) foo(10).get @@ -77,14 +77,14 @@ fun foo(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) //│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) foo(10).get diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index 9bcc79442d..7f681dcdae 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -15,14 +15,14 @@ fun f(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1440) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1440) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1460) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:936) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:943) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:639) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) //│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index cda81b5bc7..dad715f9b4 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -57,12 +57,7 @@ class A with g (new A).x() //│ JS (unsanitized): -//│ let g, A3, tmp1, g$; -//│ g$ = function g$() { -//│ return () => { -//│ return g() -//│ } -//│ }; +//│ let g, A3, tmp1; //│ g = function g() { //│ return 2 //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls index 1904673708..57028cbdfe 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls @@ -16,10 +16,10 @@ module A with //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1471) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1470) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 853cfff39b..6c98e61c57 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -281,7 +281,7 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let h5, f15, g13, tmp8, Capture$scope07, f$1, h$5; +//│ let h5, f15, g13, tmp8, Capture$scope07, f$1, h$4; //│ (class Capture$scope06 { //│ static { //│ Capture$scope07 = this @@ -292,7 +292,7 @@ g()(1) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); -//│ h$5 = function h$(scope0$cap, x1, k) { +//│ h$4 = function h$(scope0$cap, x1, k) { //│ return () => { //│ return h5(scope0$cap, x1, k) //│ } @@ -405,8 +405,8 @@ fun f(x) = set y = 2 [g, g] //│ JS (unsanitized): -//│ let g19, f25, g$16; -//│ g$16 = function g$(y1) { +//│ let g19, f25, g$14; +//│ g$14 = function g$(y1) { //│ return () => { //│ return g19(y1) //│ } @@ -419,12 +419,12 @@ fun f(x) = //│ scrut = x1 < 0; //│ if (scrut === true) { //│ y1 = 1; -//│ g$here = g$16(y1); +//│ g$here = g$14(y1); //│ return globalThis.Object.freeze([ //│ g$here, //│ g$here //│ ]) -//│ } else { y1 = 2; g$here = g$16(y1); return globalThis.Object.freeze([ g$here, g$here ]) } +//│ } else { y1 = 2; g$here = g$14(y1); return globalThis.Object.freeze([ g$here, g$here ]) } //│ }; :sjs @@ -434,8 +434,8 @@ fun f(x) = set x += 1 [a, g] //│ JS (unsanitized): -//│ let g20, f26, Capture$f7, g$17; -//│ g$17 = function g$(f$cap) { +//│ let g20, f26, Capture$f7, g$15; +//│ g$15 = function g$(f$cap) { //│ return () => { //│ return g20(f$cap) //│ } @@ -456,11 +456,11 @@ fun f(x) = //│ f26 = function f(x1) { //│ let a1, tmp11, f$cap, g$here; //│ f$cap = new Capture$f7(x1); -//│ g$here = g$17(f$cap); +//│ g$here = g$15(f$cap); //│ a1 = g$here; //│ tmp11 = f$cap.x$0 + 1; //│ f$cap.x$0 = tmp11; -//│ g$here = g$17(f$cap); +//│ g$here = g$15(f$cap); //│ return globalThis.Object.freeze([ a1, g$here ]) //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls index 9e2c1d43f4..04a083b898 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -14,10 +14,10 @@ module A with //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1410) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1471) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1470) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index e0db79b6c1..1717a92868 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -14,7 +14,22 @@ fun foo(x, y) = :expect 14 foo(10, 0) -//│ = 14 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' +//│ at M.foo (REPL10:1:447) +//│ at foo (REPL10:1:758) +//│ at REPL13:1:38 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' // * This works the same with classes. // * Note that we currently generate accessors, which is not ideal; @@ -27,16 +42,7 @@ fun foo(y) = (new M).foo() foo(10) //│ JS (unsanitized): -//│ let M3, foo1, M$; -//│ M$ = function M$(isMut, y) { -//│ let tmp; -//│ if (isMut === true) { -//│ tmp = new M3(y); -//│ } else { -//│ tmp = globalThis.Object.freeze(new M3(y)); -//│ } -//│ return tmp -//│ }; +//│ let M3, foo1; //│ (class M2 { //│ static { //│ M3 = this @@ -44,9 +50,7 @@ foo(10) //│ constructor(y) { //│ this.y = y; //│ } -//│ #y; -//│ get y() { return this.#y; } -//│ set y(value) { this.#y = value; } +//│ #M$cap; //│ foo() { //│ this.y = 2; //│ return runtime.Unit @@ -54,8 +58,25 @@ foo(10) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); -//│ foo1 = function foo(y) { let tmp; tmp = M$(false, y); return tmp.foo() }; +//│ foo1 = function foo(y) { +//│ let tmp; +//│ tmp = globalThis.Object.freeze(new M3(y)); +//│ return tmp.foo() +//│ }; //│ foo1(10) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' +//│ at M2.foo (REPL16:1:283) +//│ at foo (REPL16:1:602) +//│ at REPL16:1:643 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) // * `new mut` (or lack thereof) should be preserved by the lifter @@ -70,9 +91,12 @@ let cc = foo(10) c0 = cc.0 c1 = cc.1 -//│ c0 = C -//│ c1 = C -//│ cc = [C, C] +//│ c0 = C { foo$cap: Capture$foo { y$0: 10 } } +//│ c1 = C { foo$cap: Capture$foo { y$0: 10 } } +//│ cc = [ +//│ C { foo$cap: Capture$foo { y$0: 10 } }, +//│ C { foo$cap: Capture$foo { y$0: 10 } } +//│ ] :re set c0.x = 1 @@ -93,7 +117,7 @@ fun foo(y) = O let o = foo(10) -//│ o = O +//│ o = O { foo$cap: undefined, class: object O } :re set o.x = 1 @@ -108,53 +132,61 @@ fun foo(x, y) = x + y fun foo3 = M.foo2() foo3 -//│ JS (unsanitized): -//│ let M5, foo4, foo3$, foo$capture3; -//│ foo3$ = function foo3$(M6, x, foo$capture4) { -//│ return M6.foo2() -//│ }; -//│ (class M4 { -//│ static { -//│ M5 = this -//│ } -//│ constructor(x, foo$capture4) { -//│ this.foo$capture = foo$capture4; -//│ this.x = x; -//│ } -//│ #foo$capture; -//│ #x; -//│ get foo$capture() { return this.#foo$capture; } -//│ set foo$capture(value) { this.#foo$capture = value; } -//│ get x() { return this.#x; } -//│ set x(value) { this.#x = value; } -//│ foo2() { -//│ this.foo$capture.y$capture$0 = 2; -//│ return this.x + this.foo$capture.y$capture$0 -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "M"]; -//│ }); -//│ (class foo$capture2 { -//│ static { -//│ foo$capture3 = this -//│ } -//│ constructor(y$capture$0) { -//│ this.y$capture$0 = y$capture$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "foo$capture"]; -//│ }); -//│ foo4 = function foo(x, y) { -//│ let tmp, M$1, capture; -//│ capture = new foo$capture3(y); -//│ M$1 = globalThis.Object.freeze(new M5(x, capture)); -//│ tmp = foo3$(M$1, x, capture); -//│ return tmp -//│ }; +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.util.NoSuchElementException: key not found: object:M +//│ at: scala.collection.immutable.Map$Map3.apply(Map.scala:417) +//│ at: hkmc2.Lifter$$anon$3.applyPath(Lifter.scala:537) +//│ at: hkmc2.codegen.BlockTransformer.applyResult(BlockTransformer.scala:136) +//│ at: hkmc2.Lifter$$anon$3.applyResult(Lifter.scala:508) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:27) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:307) +//│ at: hkmc2.Lifter$BlockRewriter.rewriteBms(Lifter.scala:548) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:560) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) +//│ at: hkmc2.Lifter$LiftedFunc.mkFlattenedDefn(Lifter.scala:1238) :expect 12 foo(10, 0) -//│ = 12 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:foo +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, cc -> cc, c0 -> c0, c1 -> c1, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:O -> O1, member:foo -> foo3, object:O -> O, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, $block$res -> block$res, member:M -> M1, member:foo -> foo, member:Capture$foo -> Capture$foo3, object:M -> M, o -> o, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, $block$res -> block$res1, $block$res -> block$res2, member:M -> M3, member:foo -> foo1, member:Predef -> Predef, class:M -> M2, $block$res -> block$res11, $block$res -> block$res3, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, class:Capture$foo -> Capture$foo) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.156: foo(10, 0) +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.104: fun foo(x, y) = +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.105: object M with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.106: fun foo2() = +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.107: set y = 2 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.108: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' +//│ at M.foo (REPL10:1:447) +//│ at foo (REPL10:1:758) +//│ at REPL44:1:39 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' data class A(x) with @@ -164,7 +196,22 @@ data class A(x) with :expect 2 A(2).getA() -//│ = 2 +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'getB') +//│ at A.getA (REPL47:1:899) +//│ at REPL50:1:83 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' // Classes and functions in modules // @@ -175,7 +222,23 @@ module M with fun get = x val hi = A() M.hi.get -//│ = 2 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.172: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :expect 2 module M with @@ -184,7 +247,23 @@ module M with fun g() = x g M.f()() -//│ = 2 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.181: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :expect 2 object M with @@ -210,45 +289,26 @@ module M with fun get = M.x + x val x = if A() is A then 2 else 3 M.A().get -//│ JS (unsanitized): -//│ let M17, tmp3; -//│ (class M16 { -//│ static { -//│ M17 = this -//│ } -//│ constructor() { -//│ runtime.Unit; -//│ } -//│ static { -//│ let scrut, tmp4; -//│ this.A = function A() { -//│ return globalThis.Object.freeze(new A.class()); -//│ }; -//│ (class A5 { -//│ static { -//│ M16.A.class = this -//│ } -//│ constructor() {} -//│ get get() { -//│ return M17.x + M16.x; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A", []]; -//│ }); -//│ scrut = M16.A(); -//│ if (scrut instanceof M16.A.class) { -//│ tmp4 = 2; -//│ } else { -//│ tmp4 = 3; -//│ } -//│ this.x = tmp4; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "M"]; -//│ }); -//│ tmp3 = M17.A(); -//│ tmp3.get -//│ = 4 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.208: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyCase (Lifter.scala:311) +//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module M with val x = 2 @@ -256,7 +316,23 @@ module M with fun get = x class B() extends A M.B().get -//│ = 2 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.253: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) /// Top Level Modules /// @@ -273,11 +349,47 @@ module M with fun newB = B() 0 is A M.A(2).newB.newA_B(3).newB.get -//│ = 3 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.267: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyCase (Lifter.scala:311) +//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module M with class A object B extends A +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.278: module M with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :lift @@ -286,23 +398,99 @@ fun foo(x) = fun bar = x val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x$0') +//│ at new O2 (REPL59:1:194) +//│ at (REPL59:1:85) +//│ at REPL59:1:419 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) foo(123) -//│ = [true, [true, false], true] +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] TypeError: foo4 is not a function +//│ at REPL62:1:39 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) module A with fun g = 1 fun f = () => g f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.293: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module A with val a = (() => 1)() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.298: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // This is related to the above, but it will look into the ignored bms map module A with val x = fun f() = 1 f() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.302: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module A with val x = @@ -312,7 +500,23 @@ module A with h() f() + g() A.x -//│ = 2 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.307: module A with +//│ ╙── ^ +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: scala.collection.immutable.List.foreach(List.scala:323) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) +//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // private fields in modules :fixme @@ -322,7 +526,9 @@ module A with fun nested() = x nested() A.mtd -//│ ═══[COMPILATION ERROR] Uses of private fields cannot yet be lifted. -//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.319: module A with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls index 6d04f22445..2bea555fc1 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls @@ -99,13 +99,6 @@ fun foo() = x bar //│ JS (unsanitized): -//│ let bar4, foo4, bar$4; -//│ bar$4 = function bar$() { -//│ return () => { -//│ return bar4() -//│ } -//│ }; -//│ bar4 = function bar() { let x; return x }; -//│ foo4 = function foo() { return bar4 }; +//│ let bar4, foo4; bar4 = function bar() { let x; return x }; foo4 = function foo() { return bar4 }; diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index c19e96a031..885104b754 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -165,13 +165,10 @@ module A with fun g(x) = if x < 0 then 0 else @tailcall f(x) @tailcall g(x - 1) A.f(10000) -//│ ╔══[ERROR] This tail call exits the current scope is not optimized. -//│ ║ l.165: fun g(x) = if x < 0 then 0 else @tailcall f(x) -//│ ╙── ^^^ -//│ ╔══[ERROR] This tail call exits the current scope is not optimized. -//│ ║ l.166: @tailcall g(x - 1) -//│ ╙── ^^^^^^^^ -//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.163: module A with +//│ ╙── ^ +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing // These calls are represented as field selections and don't yet have the explicitTailCall parameter. :breakme From 494d6a07759675519bf1be0843600a73840627d2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 17 Jan 2026 17:50:10 +0800 Subject: [PATCH 10/18] progress --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 3 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 2 + hkmc2/shared/src/test/mlscript/HkScratch.mls | 2 +- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 141 ++++++++- .../src/test/mlscript/backlog/Lifter.mls | 45 ++- .../src/test/mlscript/handlers/Debugging.mls | 17 +- .../src/test/mlscript/handlers/Effects.mls | 28 +- .../test/mlscript/handlers/EffectsHygiene.mls | 12 +- .../mlscript/handlers/EffectsInClasses.mls | 10 +- .../test/mlscript/handlers/NestedHandlers.mls | 288 ++++++++++++++++++ .../mlscript/handlers/NonLocalReturns.mls | 195 ++++++++---- .../mlscript/lifter/ClassWithCompanion.mls | 32 +- .../test/mlscript/lifter/CompanionsInFun.mls | 16 +- .../src/test/mlscript/lifter/DefnsInClass.mls | 59 ++-- .../test/mlscript/lifter/EffectHandlers.mls | 8 +- .../src/test/mlscript/lifter/Imports.mls | 8 +- .../test/mlscript/lifter/ModulesObjects.mls | 191 ++++++------ .../src/test/mlscript/staging/Syntax.mls | 6 +- .../src/test/mlscript/tailrec/TailRecOpt.mls | 6 +- 19 files changed, 762 insertions(+), 307 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index d8a08b223c..b40b40e0fa 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -175,10 +175,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // use a map to convert val moduleObjs = nestedScopeNodes.collect: - case ScopeNode(obj = o: ScopedObject.Companion) => o + case s @ ScopeNode(obj = o: ScopedObject.Companion) if !s.isTopLevel => o // TODO: refine handling of companions for m <- moduleObjs do + println(m.node.get.parent.get.obj) ignored += m.par.isym ignored += m.comp.isym raise(WarningReport( diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 301ca7b969..600572690c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -439,6 +439,8 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes for l <- reads do if hasMutator.contains(l) then reqCapture += l + if mutated.contains(l) && !linearVars.contains(l) then + reqCapture += l hasReader += l // if this defn calls another defn that creates a class or has a naked reference to a // function, we must capture the latter's mutated variables in a capture, as arbitrarily diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 1e7d2b3a24..8b8d516d49 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -32,7 +32,7 @@ handle h3 = Eff with k() h1.perform() h3.perform() -//│ Elab: { handle h1‹694› = Ref(member:Eff‹686›)() List(HandlerTermDefinition(k‹699›,TermDefinition(Fun,member:perform‹698›,term:class:Handler$h1$‹695›.perform‹704›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h1)),None))))),App(Ref(k‹699›),Tup(List())))),‹result of member:perform‹698››‹703›,‹method ›,Modulefulness(None),List(),None))) in Handle(h2‹705›,Ref(member:Eff‹686›),List(),class:Handler$h2$‹706›,List(HandlerTermDefinition(k‹710›,TermDefinition(Fun,member:perform‹709›,term:class:Handler$h2$‹706›.perform‹716›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h2)),None)))), App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Ref(k‹710›),Tup(List())))),‹result of member:perform‹709››‹715›,‹method ›,Modulefulness(None),List(),None))),Handle(h3‹717›,Ref(member:Eff‹686›),List(),class:Handler$h3$‹718›,List(HandlerTermDefinition(k‹722›,TermDefinition(Fun,member:perform‹721›,term:class:Handler$h3$‹718›.perform‹728›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef‹726›),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h3)),None)))), App(Sel(Ref(h2‹705›),Ident(perform)),Tup(List()))),App(Ref(k‹722›),Tup(List())))),‹result of member:perform‹721››‹727›,‹method ›,Modulefulness(None),List(),None))),Blk(List(App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Sel(Ref(h3‹717›),Ident(perform)),Tup(List()))))) } +//│ Elab: { handle h1‹694› = Ref(member:Eff‹686›)() List(HandlerTermDefinition(k‹699›,TermDefinition(Fun,member:perform‹698›,term:class:Handler$h1$‹695›.perform‹704›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h1)),None))))),App(Ref(k‹699›),Tup(List())))),‹result of member:perform‹698››‹703›,‹method ›,Modulefulness(None),List(),None))) in Handle(h2‹705›,Ref(member:Eff‹686›),List(),class:Handler$h2$‹706›,List(HandlerTermDefinition(k‹710›,TermDefinition(Fun,member:perform‹709›,term:class:Handler$h2$‹706›.perform‹716›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h2)),None)))), App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Ref(k‹710›),Tup(List())))),‹result of member:perform‹709››‹715›,‹method ›,Modulefulness(None),List(),None))),Handle(h3‹717›,Ref(member:Eff‹686›),List(),class:Handler$h3$‹718›,List(HandlerTermDefinition(k‹722›,TermDefinition(Fun,member:perform‹721›,term:class:Handler$h3$‹718›.perform‹728›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h3)),None)))), App(Sel(Ref(h2‹705›),Ident(perform)),Tup(List()))),App(Ref(k‹722›),Tup(List())))),‹result of member:perform‹721››‹727›,‹method ›,Modulefulness(None),List(),None))),Blk(List(App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Sel(Ref(h3‹717›),Ident(perform)),Tup(List()))))) } //│ Pretty Lowered: //│ //│ define fun Handler$h2$perform(k) { diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index d100ca8bce..71d8d4773b 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -13,13 +13,136 @@ class Eff //│ Elab: { Cls Eff { }; } :lift +:sjs fun f(x) = - class A with - fun getX = x - fun g = new A - g -//│ Elab: { fun member:f‹694›(x‹695›) = { Cls A { method fun member:getX‹691› = x‹695›#666; }; fun member:g‹693› = new member:A‹692›#666; member:g‹693›#666 }; } -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' -//│ ╟── which references the symbol introduced here -//│ ║ l.16: fun f(x) = -//│ ╙── ^ + while false do + let i = 0 + set x += 1 + set i += 1 + fun g() = x + i + g +//│ Elab: { fun member:f‹692›(x‹693›) = while { let scrut = false; $scrut‹695›#0 is true then { let i‹696›; i‹696› = 0; x‹693›#666 := builtin:+‹48›#0(x‹693›#666, 1); i‹696›#666 := builtin:+‹48›#1(i‹696›#666, 1); fun member:g‹691›() = builtin:+‹48›#2(x‹693›#666, i‹696›#666); member:g‹691›#666 }; else () }; } +//│ JS (unsanitized): +//│ let g, f, Capture$f1, g$; +//│ g$ = function g$(f$cap, i) { +//│ return () => { +//│ return g(f$cap, i) +//│ } +//│ }; +//│ g = function g(f$cap, i) { +//│ return f$cap.x$0 + i +//│ }; +//│ (class Capture$f { +//│ static { +//│ Capture$f1 = this +//│ } +//│ constructor(x$0) { +//│ this.x$0 = x$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$f"]; +//│ }); +//│ f = function f(x) { +//│ let tmp, f$cap; +//│ f$cap = new Capture$f1(x); +//│ lbl: while (true) { +//│ let scrut, i, tmp1, tmp2, g$here; +//│ scrut = false; +//│ if (scrut === true) { +//│ i = 0; +//│ tmp1 = f$cap.x$0 + 1; +//│ f$cap.x$0 = tmp1; +//│ tmp2 = i + 1; +//│ i = tmp2; +//│ g$here = g$(f$cap, i); +//│ tmp = g$here; +//│ continue lbl +//│ } else { tmp = runtime.Unit; } +//│ break; +//│ } +//│ return tmp +//│ }; + +:slot +class C with + let x = 2 + fun f(x) = 2 +//│ Elab: { Cls C { let term:class:C‹755›.x‹757›; term:class:C‹755›.x‹757› = 2; method fun member:f‹753›(x‹758›) = 2; }; } +//│ Pretty Lowered: +//│ +//│ define class C { +//│ let x = ... +//│ ctor { +//│ set x = 2 in +//│ end +//│ } +//│ fun class:C‹755›::f(x1) { +//│ return 2 +//│ } +//│ } in +//│ set block$res3 = undefined in +//│ end + +:slot +module M with + fun bruh = 2 + fun lol = 54 +//│ Elab: { Mod M { fun member:bruh‹765› = 2; fun member:lol‹764› = 54; }; } +//│ Pretty Lowered: +//│ +//│ define class M in +//│ module M { +//│ fun module:M‹767›::bruh { +//│ return 2 +//│ } +//│ fun module:M‹767›::lol { +//│ return 54 +//│ } +//│ } in +//│ set block$res4 = undefined in +//│ end + +:slot +class A(x) with + fun bruh = 2 + fun a = 4 +//│ Elab: { Cls AParamList(‹›,List(Param(‹›,x‹784›,None,Modulefulness(None))),None) { let term:class:A‹782›.x‹787›; term:class:A‹782›.x‹787› = x‹784›#0; method fun member:bruh‹780› = 2; method fun member:a‹779› = 4; }; } +//│ Pretty Lowered: +//│ +//│ define class A(x) { +//│ let x = ... +//│ ctor { +//│ set x1 = x in +//│ end +//│ } +//│ fun class:A‹782›::bruh { +//│ return 2 +//│ } +//│ fun class:A‹782›::a { +//│ return 4 +//│ } +//│ } in +//│ set block$res5 = undefined in +//│ end + +:slot +fun g = 1 +fun h = 4 +fun f(x) = + set x += 2 +//│ Elab: { fun member:g‹797› = 1; fun member:h‹798› = 4; fun member:f‹796›(x‹805›) = x‹805›#666 := builtin:+‹48›#3(x‹805›#666, 2); } +//│ Pretty Lowered: +//│ +//│ define fun g() { +//│ return 1 +//│ } in +//│ define fun h() { +//│ return 4 +//│ } in +//│ define fun f(x) { +//│ set tmp = +(x, 2) in +//│ set x = tmp in +//│ return runtime.Unit +//│ } in +//│ set block$res6 = undefined in +//│ end diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index d354f9c106..68e95d2665 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -141,7 +141,7 @@ module M with g M.f()() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.142: module M with +//│ ║ l.137: module M with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing @@ -156,7 +156,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.155: module M with +//│ ║ l.152: module M with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing @@ -166,15 +166,15 @@ foo(10, 0) //│ ║ l.164: foo(10, 0) //│ ║ ^^^ //│ ╟── which references the symbol introduced here -//│ ║ l.154: fun foo(x, y) = +//│ ║ l.151: fun foo(x, y) = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.155: module M with +//│ ║ l.152: module M with //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.156: val test = 2 +//│ ║ l.153: val test = 2 //│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.157: fun foo() = +//│ ║ l.154: fun foo() = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.158: ... (more lines omitted) ... +//│ ║ l.155: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined //│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' @@ -187,25 +187,25 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.168: module M with +//│ ║ l.183: module M with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 12 foo(10, 0) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.178: foo(10, 0) +//│ ║ l.195: foo(10, 0) //│ ║ ^^^ //│ ╟── which references the symbol introduced here -//│ ║ l.167: fun foo(x, y) = +//│ ║ l.182: fun foo(x, y) = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.168: module M with +//│ ║ l.183: module M with //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.169: fun foo() = +//│ ║ l.184: fun foo() = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.170: set y = 2 +//│ ║ l.185: set y = 2 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.171: ... (more lines omitted) ... +//│ ║ l.186: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -215,24 +215,21 @@ data class A(x) with module M with fun getB() = x fun getA() = M.getB() -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.184: module M with -//│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 2 A(2).getA() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.190: A(2).getA() +//│ ║ l.224: A(2).getA() //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.183: data class A(x) with +//│ ║ l.214: data class A(x) with //│ ║ ^^^^^^^^^ -//│ ║ l.184: module M with +//│ ║ l.215: module M with //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.185: fun getB() = x +//│ ║ l.216: fun getB() = x //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.186: fun getA() = M.getB() +//│ ║ l.217: fun getA() = M.getB() //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[RUNTIME ERROR] ReferenceError: A is not defined //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' @@ -246,7 +243,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.198: class Bar extends Foo +//│ ║ l.245: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. @@ -262,7 +259,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.213: module M with +//│ ║ l.259: module M with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index 9b0039af5a..2388e2b86a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -157,18 +157,13 @@ module Test with j / i fun main()(using Debugger) = test(12) + test(34) -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.151: module Test with -//│ ╙── ^^^^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1048) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1109) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1108) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -184,7 +179,7 @@ let res = using Debugger = h Runtime.try(() => Test.main()) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'Test' -//│ ║ l.185: Runtime.try(() => Test.main()) +//│ ║ l.180: Runtime.try(() => Test.main()) //│ ║ ^^^^ //│ ╟── which references the symbol introduced here //│ ║ l.151: module Test with @@ -232,8 +227,8 @@ fun f() = f() //│ > Stack Trace: -//│ > at f (Debugging.mls:226:3) with locals: j=200 +//│ > at f (Debugging.mls:221:3) with locals: j=200 //│ > Stack Trace: -//│ > at f (Debugging.mls:228:3) +//│ > at f (Debugging.mls:223:3) //│ > Stack Trace: //│ > at tail position diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 985622037c..1b91d5e920 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -278,17 +278,17 @@ module A with fun test() = x fun perform(arg) //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.275: module A with +//│ ║ l.276: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -312,16 +312,16 @@ h.perform() //│ curThis = S of S of class:Handler$h$ //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.280: handle h = A.Effect(3) with +//│ ║ l.298: handle h = A.Effect(3) with //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.275: module A with +//│ ║ l.276: module A with //│ ║ ^^^^^^ -//│ ║ l.276: data class Effect(x) with +//│ ║ l.277: data class Effect(x) with //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.277: fun test() = x +//│ ║ l.278: fun test() = x //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.278: fun perform(arg) +//│ ║ l.279: fun perform(arg) //│ ╙── ^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) @@ -497,16 +497,16 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.437: module A with +//│ ║ l.492: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.433: module A with +//│ ║ l.488: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.429: module A with +//│ ║ l.484: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.425: module A with +//│ ║ l.480: module A with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls index b19710c0a4..e50396aa3c 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls @@ -4,17 +4,17 @@ module M //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.5: module M //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -32,10 +32,10 @@ fun foo(h): module M = module A A //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.15: module A +//│ ║ l.32: module A //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.12: module A +//│ ║ l.29: module A //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index dadfc26c54..cee78a7d5e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -119,17 +119,17 @@ module A with f() () // defeats tail call optimization //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.117: module A with +//│ ║ l.118: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 392d5fb2a6..89edec5eca 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -9,6 +9,7 @@ let id = 0 class MaybeStop with fun f(x: Bool): () +:slot fun handleEffects(g) = handle h1 = MaybeStop with fun f(x)(k) = @@ -33,6 +34,293 @@ fun handleEffects(g) = print("h2 end " + String(cur)) result g(h1, h2) +//│ Pretty Lowered: +//│ +//│ define fun Handler$h1$f$(x)(k) { +//│ return Handler$h1$f(x, k) +//│ } in +//│ define fun Handler$h1$f(x1, k1) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc = 0 in +//│ end +//│ else +//│ set pc = runtime.resumePc in +//│ set saveOffset = runtime.resumeIdx in +//│ set cur = runtime.resumeArr.saveOffset in +//│ set saveOffset = +(saveOffset, 1) in +//│ set result = runtime.resumeArr.saveOffset in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main = match pc +//│ 0 => +//│ match x1 +//│ true => +//│ return Predef.print("h1 stop") +//│ else +//│ set tmp = +(id, 1) in +//│ set id = tmp in +//│ set cur = id in +//│ set runtime.resumeValue = globalThis.String(cur) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$f, 7, "NestedHandlers.mls:21:29", null, null, 1, 2, x1, k1, 2, cur, result) +//│ in +//│ set pc = 7 in +//│ continue main +//│ in +//│ end +//│ 5 => +//│ set tmp1 = runtime.resumeValue in +//│ set runtime.resumeValue = k1(x1) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$f, 4, null, null, null, 1, 2, x1, k1, 2, cur, result) +//│ in +//│ set pc = 4 in +//│ continue main +//│ 1 => +//│ set tmp2 = runtime.resumeValue in +//│ return result +//│ 6 => +//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$f, 5, "NestedHandlers.mls:21:9", null, null, 1, 2, x1, k1, 2, cur, result) +//│ in +//│ set pc = 5 in +//│ continue main +//│ 2 => +//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$f, 1, "NestedHandlers.mls:23:9", null, null, 1, 2, x1, k1, 2, cur, result) +//│ in +//│ set pc = 1 in +//│ continue main +//│ 7 => +//│ set tmp3 = runtime.resumeValue in +//│ set tmp4 = +("h1 start ", tmp3) in +//│ set pc = 6 in +//│ continue main +//│ 3 => +//│ set tmp5 = runtime.resumeValue in +//│ set tmp6 = +("h1 end ", tmp5) in +//│ set pc = 2 in +//│ continue main +//│ 4 => +//│ set result = runtime.resumeValue in +//│ set runtime.resumeValue = globalThis.String(cur) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h1$f, 3, "NestedHandlers.mls:23:27", null, null, 1, 2, x1, k1, 2, cur, result) +//│ in +//│ set pc = 3 in +//│ continue main +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun Handler$h2$f$(x2)(k2) { +//│ return Handler$h2$f(x2, k2) +//│ } in +//│ define fun Handler$h2$f(x3, k3) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc1 = 0 in +//│ end +//│ else +//│ set pc1 = runtime.resumePc in +//│ set saveOffset1 = runtime.resumeIdx in +//│ set cur1 = runtime.resumeArr.saveOffset1 in +//│ set saveOffset1 = +(saveOffset1, 1) in +//│ set result1 = runtime.resumeArr.saveOffset1 in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main1 = match pc1 +//│ 0 => +//│ match x3 +//│ true => +//│ return Predef.print("h2 stop") +//│ else +//│ set tmp7 = +(id, 1) in +//│ set id = tmp7 in +//│ set cur1 = id in +//│ set runtime.resumeValue = globalThis.String(cur1) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$f, 7, "NestedHandlers.mls:32:29", null, null, 1, 2, x3, k3, 2, cur1, result1) +//│ in +//│ set pc1 = 7 in +//│ continue main1 +//│ in +//│ end +//│ 5 => +//│ set tmp8 = runtime.resumeValue in +//│ set runtime.resumeValue = k3(x3) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$f, 4, null, null, null, 1, 2, x3, k3, 2, cur1, result1) +//│ in +//│ set pc1 = 4 in +//│ continue main1 +//│ 1 => +//│ set tmp9 = runtime.resumeValue in +//│ return result1 +//│ 6 => +//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$f, 5, "NestedHandlers.mls:32:9", null, null, 1, 2, x3, k3, 2, cur1, result1) +//│ in +//│ set pc1 = 5 in +//│ continue main1 +//│ 2 => +//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$f, 1, "NestedHandlers.mls:34:9", null, null, 1, 2, x3, k3, 2, cur1, result1) +//│ in +//│ set pc1 = 1 in +//│ continue main1 +//│ 7 => +//│ set tmp10 = runtime.resumeValue in +//│ set tmp11 = +("h2 start ", tmp10) in +//│ set pc1 = 6 in +//│ continue main1 +//│ 3 => +//│ set tmp12 = runtime.resumeValue in +//│ set tmp13 = +("h2 end ", tmp12) in +//│ set pc1 = 2 in +//│ continue main1 +//│ 4 => +//│ set result1 = runtime.resumeValue in +//│ set runtime.resumeValue = globalThis.String(cur1) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(Handler$h2$f, 3, "NestedHandlers.mls:34:27", null, null, 1, 2, x3, k3, 2, cur1, result1) +//│ in +//│ set pc1 = 3 in +//│ continue main1 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun handleBlock$$(g, h1, h2)() { +//│ return handleBlock$(g, h1, h2) +//│ } in +//│ define class Handler$h2$ { +//│ let Handler$h2$$cap = ... +//│ fun class:Handler$h2$ :: f(x4) { +//│ set Handler$h2$f$here = Handler$h2$f$(x4) in +//│ return runtime.mkEffect(Handler$h2$, Handler$h2$f$here) +//│ } +//│ } in +//│ define fun handleBlock$(g1, h11, h21) { +//│ return g1(h11, h21) +//│ } in +//│ define fun handleBlock$$(g2, h12)() { +//│ return handleBlock$(g2, h12) +//│ } in +//│ define class Handler$h1$ { +//│ let Handler$h1$$cap = ... +//│ fun class:Handler$h1$ :: f(x5) { +//│ set Handler$h1$f$here = Handler$h1$f$(x5) in +//│ return runtime.mkEffect(Handler$h1$, Handler$h1$f$here) +//│ } +//│ } in +//│ define fun handleBlock$(g3, h13) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc2 = 0 in +//│ end +//│ else +//│ set pc2 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main2 = match pc2 +//│ 0 => +//│ set h22 = new mut Handler$h2$() in +//│ set handleBlock$$here = handleBlock$$(g3, h13, h22) in +//│ set runtime.resumeValue = runtime.enterHandleBlock(h22, handleBlock$$here) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 2, g3, h13, 0) +//│ in +//│ set pc2 = 1 in +//│ continue main2 +//│ 1 => +//│ set tmp14 = runtime.resumeValue in +//│ return tmp14 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ define fun handleEffects(g4) { +//│ match runtime.resumePc +//│ -1 => +//│ set pc3 = 0 in +//│ end +//│ else +//│ set pc3 = runtime.resumePc in +//│ set runtime.resumePc = -1 in +//│ end +//│ in +//│ labelled loop main3 = match pc3 +//│ 0 => +//│ set h14 = new mut Handler$h1$() in +//│ set handleBlock$$here1 = handleBlock$$(g4, h14) in +//│ set runtime.resumeValue = runtime.enterHandleBlock(h14, handleBlock$$here1) in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ return runtime.unwind(handleEffects, 1, null, null, null, 1, 1, g4, 0) +//│ in +//│ set pc3 = 1 in +//│ continue main3 +//│ 1 => +//│ set tmp15 = runtime.resumeValue in +//│ return tmp15 +//│ else +//│ +//│ in +//│ end in +//│ end +//│ } in +//│ set block$res3 = undefined in +//│ end fun f(h1, h2) = diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index 0980562b45..ef8c666915 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -7,26 +7,75 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ = 100 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) fun foo(x) = let xs = [() => return x + 1] xs.0() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) foo(123) -//│ = 124 +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:foo +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap($block$res -> block$res1, $tmp -> tmp, member:Predef‹726› -> Predef, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' +//│ ║ l.45: foo(123) +//│ ║ ^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.26: fun foo(x) = +//│ ║ ^^^^^^^^^^^^ +//│ ║ l.27: let xs = [() => return x + 1] +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.28: xs.0() +//│ ╙── ^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ at REPL10:1:29 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) :expect 123 @@ -43,19 +92,23 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ > Ok -//│ = 123 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :expect 100 fun f() = @@ -71,50 +124,68 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ > Ok -//│ > 200 -//│ > Continue -//│ = 100 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :re fun f() = () => return 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_6 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :re fun f(): () -> Int = () => return () => 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_7 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :e return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.115: return 100 +//│ ║ l.186: return 100 //│ ╙── ^^^^^^^^^^ :e @@ -122,14 +193,14 @@ if true do while false do let f = () => return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.123: let f = () => return 100 +//│ ║ l.194: let f = () => return 100 //│ ╙── ^^^^^^^^^^ :e fun f() = type A = return "lol" //│ ╔══[ERROR] Return statements are not allowed in this context. -//│ ║ l.130: type A = return "lol" +//│ ║ l.201: type A = return "lol" //│ ╙── ^^^^^^^^^^^^ @@ -146,12 +217,20 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (HandlerLowering.scala:524) -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ = 100 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) +//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) +//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) +//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) +//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) +//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) // broken due to handlers currently not handling `object` properly :fixme @@ -162,9 +241,7 @@ fun f() = 4 f() //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'ret') -//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. // ctor cannot raise effect, so error is expected. :fixme @@ -175,9 +252,7 @@ fun f() = new A f() //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_10 is raised in a constructor -//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. :fixme :expect 100 @@ -187,9 +262,7 @@ fun f() = 4 f() //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'ret') -//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. :fixme :expect 100 @@ -200,6 +273,4 @@ fun f() = 4 f() //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ ═══[WARNING] Unexpected nested class: lambdas may not function correctly. -//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_12 is raised in a constructor -//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls index 15d7344330..2467c33377 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls @@ -15,15 +15,15 @@ fun foo(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) foo(10).get //│ FAILURE: Unexpected compilation error @@ -77,15 +77,15 @@ fun foo(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) foo(10).get //│ FAILURE: Unexpected compilation error diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index 7f681dcdae..b9dcff6da1 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -15,14 +15,14 @@ fun f(x) = //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.$anonfun$55(Lifter.scala:1437) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) //│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1437) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1457) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:933) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:940) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:636) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) +//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) +//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) +//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) +//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) +//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) +//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index dad715f9b4..0049d276f3 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -1,50 +1,39 @@ :js :lift -:sjs +:slot data class A(x) with data class B(y) with fun getB() = x + y fun getA() = B(2).getB() -//│ JS (unsanitized): -//│ let B1, A1; -//│ B1 = function B(A2, y) { -//│ return globalThis.Object.freeze(new B.class(A2, y)); -//│ }; -//│ (class B { -//│ static { -//│ B1.class = this +//│ Pretty Lowered: +//│ +//│ define class B(A, y) { +//│ let B$cap = ... +//│ y +//│ ctor { +//│ set A1 = A in +//│ define val y = y in +//│ end //│ } -//│ constructor(A2, y) { -//│ this.A = A2; -//│ this.y = y; +//│ fun class:B::getB() { +//│ return +(A.x, B.y) //│ } -//│ #B$cap; -//│ getB() { -//│ return this.A.x + this.y +//│ } in +//│ define class A(x) { +//│ let A$cap = ... +//│ x +//│ ctor { +//│ define val x = x in +//│ end //│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "B", [null, "y"]]; -//│ }); -//│ A1 = function A(x) { -//│ return globalThis.Object.freeze(new A.class(x)); -//│ }; -//│ (class A { -//│ static { -//│ A1.class = this -//│ } -//│ constructor(x) { -//│ this.x = x; -//│ } -//│ #A$cap; -//│ getA() { -//│ let tmp; -//│ tmp = B1(this, 2); +//│ fun class:A::getA() { +//│ set tmp = B(A, 2) in //│ return tmp.getB() //│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A", ["x"]]; -//│ }); +//│ } in +//│ set block$res1 = undefined in +//│ end :expect 3 A(1).getA() diff --git a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls index 57028cbdfe..a6b2e4d459 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls @@ -9,17 +9,17 @@ module A with f() val a = 1 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.7: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls index 04a083b898..8a2d594273 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -7,17 +7,17 @@ import "../../mlscript-compile/Option.mls" module A with fun f(x) = x is Option.Some //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.7: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 1717a92868..97312e6472 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -132,58 +132,49 @@ fun foo(x, y) = x + y fun foo3 = M.foo2() foo3 -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: java.util.NoSuchElementException: key not found: object:M -//│ at: scala.collection.immutable.Map$Map3.apply(Map.scala:417) -//│ at: hkmc2.Lifter$$anon$3.applyPath(Lifter.scala:537) -//│ at: hkmc2.codegen.BlockTransformer.applyResult(BlockTransformer.scala:136) -//│ at: hkmc2.Lifter$$anon$3.applyResult(Lifter.scala:508) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:27) -//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:307) -//│ at: hkmc2.Lifter$BlockRewriter.rewriteBms(Lifter.scala:548) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:560) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:480) -//│ at: hkmc2.Lifter$LiftedFunc.mkFlattenedDefn(Lifter.scala:1238) +//│ JS (unsanitized): +//│ let M5, foo31, foo4; +//│ foo31 = function foo3() { +//│ return M5.foo2() +//│ }; +//│ (class M4 { +//│ static { +//│ new this +//│ } +//│ constructor(x, y) { +//│ M5 = this; +//│ this.x = x; +//│ this.y = y; +//│ Object.defineProperty(this, "class", { +//│ value: M4 +//│ }); +//│ globalThis.Object.freeze(this); +//│ } +//│ #M$cap; +//│ foo2() { +//│ this.y = 2; +//│ return this.x + this.y +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["object", "M"]; +//│ }); +//│ foo4 = function foo(x, y) { let tmp; tmp = foo31(); return tmp }; :expect 12 foo(10, 0) -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:foo -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, cc -> cc, c0 -> c0, c1 -> c1, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:O -> O1, member:foo -> foo3, object:O -> O, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, $block$res -> block$res, member:M -> M1, member:foo -> foo, member:Capture$foo -> Capture$foo3, object:M -> M, o -> o, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, $block$res -> block$res1, $block$res -> block$res2, member:M -> M3, member:foo -> foo1, member:Predef -> Predef, class:M -> M2, $block$res -> block$res11, $block$res -> block$res3, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, class:Capture$foo -> Capture$foo) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.156: foo(10, 0) -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.104: fun foo(x, y) = -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.105: object M with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.106: fun foo2() = -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.107: set y = 2 -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.108: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M.foo (REPL10:1:447) -//│ at foo (REPL10:1:758) -//│ at REPL44:1:39 +//│ at M4.foo2 (REPL44:1:555) +//│ at foo3 (REPL44:1:141) +//│ at foo (REPL44:1:847) +//│ at REPL47:1:39 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) //│ at REPLServer.runBound [as eval] (node:domain:444:12) //│ at REPLServer.onLine (node:repl:886:12) //│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -199,8 +190,8 @@ A(2).getA() //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'getB') -//│ at A.getA (REPL47:1:899) -//│ at REPL50:1:83 +//│ at A.getA (REPL50:1:899) +//│ at REPL53:1:83 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -223,17 +214,17 @@ module M with val hi = A() M.hi.get //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.172: module M with +//│ ║ l.219: module M with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -248,17 +239,17 @@ module M with g M.f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.181: module M with +//│ ║ l.244: module M with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -290,20 +281,20 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.208: module M with +//│ ║ l.287: module M with //│ ╙── ^ //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyCase (Lifter.scala:311) +//│ FAILURE LOCATION: applyCase (Lifter.scala:199) //│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -317,17 +308,17 @@ module M with class B() extends A M.B().get //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.253: module M with +//│ ║ l.313: module M with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -350,20 +341,20 @@ module M with 0 is A M.A(2).newB.newA_B(3).newB.get //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.267: module M with +//│ ║ l.343: module M with //│ ╙── ^ //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyCase (Lifter.scala:311) +//│ FAILURE LOCATION: applyCase (Lifter.scala:199) //│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -374,17 +365,17 @@ module M with class A object B extends A //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.278: module M with +//│ ║ l.373: module M with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -401,9 +392,9 @@ fun foo(x) = //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x$0') -//│ at new O2 (REPL59:1:194) -//│ at (REPL59:1:85) -//│ at REPL59:1:419 +//│ at new O2 (REPL62:1:194) +//│ at (REPL62:1:85) +//│ at REPL62:1:419 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -415,8 +406,8 @@ fun foo(x) = foo(123) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: foo4 is not a function -//│ at REPL62:1:39 +//│ ═══[RUNTIME ERROR] TypeError: foo5 is not a function +//│ at REPL65:1:39 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -432,17 +423,17 @@ module A with fun f = () => g f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.293: module A with +//│ ║ l.430: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -452,17 +443,17 @@ module A with module A with val a = (() => 1)() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.298: module A with +//│ ║ l.452: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -475,17 +466,17 @@ module A with fun f() = 1 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.302: module A with +//│ ║ l.473: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -501,17 +492,17 @@ module A with f() + g() A.x //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:296) +//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.307: module A with +//│ ║ l.495: module A with //│ ╙── ^ //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing //│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1407) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1468) +//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) +//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) //│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1467) +//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) //│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) //│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) //│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) @@ -527,7 +518,7 @@ module A with nested() A.mtd //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.319: module A with +//│ ║ l.523: module A with //│ ╙── ^ //│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/staging/Syntax.mls b/hkmc2/shared/src/test/mlscript/staging/Syntax.mls index 94accabd06..aa471678e9 100644 --- a/hkmc2/shared/src/test/mlscript/staging/Syntax.mls +++ b/hkmc2/shared/src/test/mlscript/staging/Syntax.mls @@ -21,8 +21,6 @@ staged fun f() = 0 :staging :w staged module A -//│ ╔══[WARNING] `staged` keyword doesn't do anything currently. -//│ ║ l.23: staged module A -//│ ╙── ^^^^^^^^ //│ Pretty Lowered: -//│ define staged class A in set block$res = undefined in end +//│ define class A in set block$res = undefined in end +//│ FAILURE: Unexpected lack of warnings diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index 885104b754..4c87b65729 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -193,12 +193,12 @@ class A with fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.195: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Class methods may not yet be marked @tailrec. -//│ ║ l.195: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^ //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.196: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) +//│ ║ l.193: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //│ ╙── ^^^^^^^^ //│ = 0 From 031db5d13203cd494e5c6494a2539b0ffeaa3033 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 18 Jan 2026 18:45:22 +0800 Subject: [PATCH 11/18] fix a lot of tests, modules --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 295 +++++--- .../main/scala/hkmc2/codegen/ScopeData.scala | 72 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 15 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 81 ++- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 160 +---- .../src/test/mlscript/backlog/Lifter.mls | 87 +-- .../src/test/mlscript/handlers/Debugging.mls | 48 +- .../src/test/mlscript/handlers/Effects.mls | 66 +- .../test/mlscript/handlers/EffectsHygiene.mls | 23 +- .../mlscript/handlers/EffectsInClasses.mls | 17 - .../mlscript/handlers/HandlersScratch.mls | 92 --- .../test/mlscript/handlers/NestedHandlers.mls | 34 +- .../mlscript/handlers/NonLocalReturns.mls | 20 +- .../mlscript/handlers/ReturnInHandler.mls | 4 +- .../test/mlscript/handlers/StackSafety.mls | 18 +- .../src/test/mlscript/lifter/ClassInFun.mls | 25 +- .../mlscript/lifter/ClassWithCompanion.mls | 102 +-- .../test/mlscript/lifter/CompanionsInFun.mls | 42 +- .../src/test/mlscript/lifter/DefnsInClass.mls | 44 +- .../test/mlscript/lifter/EffectHandlers.mls | 17 - .../src/test/mlscript/lifter/Imports.mls | 38 +- .../shared/src/test/mlscript/lifter/Loops.mls | 34 +- .../test/mlscript/lifter/ModulesObjects.mls | 663 +++++++++++------- .../src/test/mlscript/lifter/PatternInFun.mls | 2 +- .../src/test/mlscript/tailrec/TailRecOpt.mls | 13 +- 25 files changed, 942 insertions(+), 1070 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index b40b40e0fa..661dd27def 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -17,6 +17,12 @@ import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ListBuffer +import hkmc2.ScopeData.ScopedObject.Top +import hkmc2.ScopeData.ScopedObject.Companion +import hkmc2.ScopeData.ScopedObject.ClassCtor +import hkmc2.ScopeData.ScopedObject.Func +import hkmc2.ScopeData.ScopedObject.Loop +import hkmc2.ScopeData.ScopedObject.ScopedBlock object Lifter: @@ -117,37 +123,45 @@ class Lifter(topLevelBlk: Block)(using State, Raise): extension (l: Local) def asLocalPath: LocalPath = LocalPath.Sym(l) + def asDefnRef: DefnRef = DefnRef.Sym(l) enum LocalPath: case Sym(l: Local) case BmsRef(l: BlockMemberSymbol, d: DefinitionSymbol[?]) case InCapture(capturePath: Path, field: TermSymbol) - case PubField(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol) + case PubField(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol, tsym: TermSymbol) - def read = this match + def read(using ctx: LifterCtxNew): Path = this match case Sym(l) => l.asPath case BmsRef(l, d) => Value.Ref(l, S(d)) case InCapture(path, field) => Select(path, field.id)(S(field)) - case PubField(isym, sym) => Select(isym.asPath, Tree.Ident(sym.nme))(N) + case PubField(isym, sym, tsym) => Select(ctx.symbolsMap(isym).read, Tree.Ident(sym.nme))(S(tsym)) - def asArg = read.asArg + def asArg(using ctx: LifterCtxNew) = read.asArg - def assign(value: Result, rest: Block) = this match + def assign(value: Result, rest: Block)(using ctx: LifterCtxNew): Block = this match case Sym(l) => Assign(l, value, rest) case BmsRef(l, d) => lastWords("Tried to assign to a BlockMemberSymbol") case InCapture(path, field) => AssignField(path, field.id, value, rest)(S(field)) - case PubField(isym, sym) => AssignField(isym.asPath, Tree.Ident(sym.nme), value, rest)(S(sym)) + case PubField(isym, sym, tsym) => AssignField(ctx.symbolsMap(isym).read, Tree.Ident(sym.nme), value, rest)(S(tsym)) + + enum DefnRef: + case Sym(l: Local) + case InScope(l: BlockMemberSymbol, d: DefinitionSymbol[?]) + case Field(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, l: BlockMemberSymbol, d: DefinitionSymbol[?]) + + def read(using ctx: LifterCtxNew): Path = this match + case Sym(l) => l.asPath + case InScope(l, d) => Value.Ref(l, S(d)) + case Field(isym, l, d) => Select(ctx.symbolsMap(isym).read, Tree.Ident(l.nme))(S(d)) + + def asArg(using ctx: LifterCtxNew) = read.asArg case class FunSyms[T <: DefinitionSymbol[?]](b: BlockMemberSymbol, d: T): def asPath = Value.Ref(b, S(d)) object FunSyms: def fromFun(b: BlockMemberSymbol, owner: Opt[InnerSymbol] = N) = FunSyms(b, TermSymbol.fromFunBms(b, owner)) - - case class Lifted[+T <: Defn]( - val liftedDefn: T, - val extraDefns: List[Defn], - ) type ClsLikeSym = DefinitionSymbol[? <: ClassDef | ModuleOrObjectDef] type ClsSym = DefinitionSymbol[? <: ClassLikeDef] @@ -179,7 +193,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // TODO: refine handling of companions for m <- moduleObjs do - println(m.node.get.parent.get.obj) ignored += m.par.isym ignored += m.comp.isym raise(WarningReport( @@ -196,7 +209,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls: (ClassSymbol | ModuleOrObjectSymbol), _) => - if nestedScopes.contains(cls) && !ignored.contains(cls) then // don't generate a warning if it's already ignored + if nestedScopes.contains(cls) && !ignored.contains(cls) && !data.getNode(cls).isInTopLevelMod then // don't generate a warning if it's already ignored raise(WarningReport( msg"Cannot yet lift class/module `${cls.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation @@ -252,15 +265,19 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case _ => false override def applyValue(v: Value): Unit = v match - case RefOfBms(_, S(l: ClassSymbol)) if nestedScopes.contains(l) => - raise(WarningReport( - msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, - N, Diagnostic.Source.Compilation - )) - ignored += l - case RefOfBms(_, S(t: TermSymbol)) => - // naked reference to a function definition - firstClsFns += t + case RefOfBms(_, S(l)) if nestedScopes.contains(l) => data.getNode(l).obj match + case c: (ScopedObject.Class | ClassCtor) => + if !c.node.get.isInTopLevelMod then + raise(WarningReport( + msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) + val isym = c match + case c: ScopedObject.Class => c.cls.isym + case c: ClassCtor => c.cls.isym + ignored += isym + case Func(fun, isMethod) => firstClsFns += fun.dSym + case _ => super.applyValue(v) case _ => super.applyValue(v) // analyze the extends graph @@ -316,26 +333,41 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version - case c @ Call(RefOfBms(l, S(d)), args) => + case c @ Call(RefOfBms(l, S(d)), args) => + def join = ctx.defnsMap.get(d) match + case Some(value) => c.copy(fun = value.read)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) + case None => c val newCall = ctx.rewrittenScopes.get(d) match case None => c case Some(value) => value match - case f: LiftedFunc => f.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap) + // function call + case f: LiftedFunc => f.rewriteCall(c) + // ctor call (without using `new`) case ctor: RewrittenClassCtor => ctor.getRewrittenCls match case cls: LiftedClass => - cls.rewriteCall(c, ctx.capturesMap, ctx.symbolsMap) - case _ => c - case _ => c + cls.rewriteCall(c) + case _ => ctx.defnsMap.get(d) match + case Some(value) => join + case None => c + case _ => join applyArgs(newCall.args): newArgs => if (newCall.args is newArgs) && (c is newCall) then k(newCall) else k(Call(newCall.fun, newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) - case inst @ Instantiate(mut, InstSel(l, S(d)), args) => ctx.rewrittenScopes.get(d) match - case S(c: LiftedClass) => - val newInst = c.rewriteInstantiate(inst, ctx.capturesMap, ctx.symbolsMap) - applyArgs(newInst.args): newArgs => - if (newInst.args is newArgs) && (newInst is inst) then k(newInst) - else k(Instantiate(newInst.mut, newInst.cls, newArgs)) - case _ => super.applyResult(r)(k) + case inst @ Instantiate(mut, RefOfBms(l, S(d)), args) => + // It is VERY IMPORTANT that we rewrite it like this and not using super.applyResult. + // The reason is that Instantiate is disambiguated using the class's InnerSymbol, which is also + // used to represent the class's `this`. super.applyResult would apply super.applyPath on the + // disambiguated BMS ref, which would replace it with the InnerSymbol, since the class scoped object + // adds `this -> this` to the symbols map. + val newInst = ctx.rewrittenScopes.get(d) match + case S(c: LiftedClass) => c.rewriteInstantiate(inst) + case _ => ctx.defnsMap.get(d) match + case Some(value) => Instantiate(inst.mut, value.read, inst.args) + case None => inst + + applyArgs(newInst.args): newArgs => + if (newInst.args is newArgs) && (newInst is inst) then k(newInst) + else k(Instantiate(newInst.mut, newInst.cls, newArgs)) case _ => super.applyResult(r)(k) // extract the call @@ -360,14 +392,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): value k(Value.Ref(newSym, N)) - case S(f: RewrittenFunc) if f.obj.isMethod => - val sel = Select(ctx.symbolsMap(f.obj.fun.owner.get).read, Tree.Ident(l.nme))(S(d)) - k(sel) - - case S(_) => super.applyPath(p)(k) - // Other naked references to BlockMemberSymbols. - case N => ctx.symbolsMap.get(d) match + case _ => ctx.defnsMap.get(d) match case Some(value) => k(value.read) case None => super.applyPath(p)(k) @@ -389,7 +415,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val pre = syms.foldLeft(blockBuilder): case (blk, (funSym, local)) => ctx.liftedScopes(funSym.d) match - case l: LiftedFunc => blk.assign(local, l.rewriteRef(ctx.capturesMap, ctx.symbolsMap)) + case l: LiftedFunc => blk.assign(local, l.rewriteRef) + case _ => die // Rewrite the rest val remaining = rewritten match @@ -484,7 +511,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): println(")") - + /* println("accessesShallow") printMap(usedVars.shallowAccesses) println("accesses") @@ -492,20 +519,14 @@ class Lifter(topLevelBlk: Block)(using State, Raise): printMap(usedVars.accessMapWithIgnored) println("usedVars") printMap(usedVars.reqdCaptures) - - - - - def isIgnored(d: Defn) = d match - case f: FunDefn => ignored.contains(f.dSym) - case v: ValDefn => true - case c: ClsLikeDefn => ignored.contains(c.isym) + */ case class LifterResult[+T](liftedDefn: T, extraDefns: List[Defn]) case class LifterCtxNew( liftedScopes: MutMap[LiftedSym, LiftedScope[?]] = MutMap.empty, rewrittenScopes: MutMap[ScopedInfo, RewrittenScope[?]] = MutMap.empty, var symbolsMap: Map[Local, LocalPath] = Map.empty, + var defnsMap: Map[DefinitionSymbol[?], DefnRef] = Map.empty, var capturesMap: Map[ScopedInfo, Path] = Map.empty ) @@ -605,10 +626,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val newCls = applyRewrittenScope(ctx.rewrittenScopes(c.isym)) match case c: ClsLikeDefn => c case _ => die - val newComp = c.companion.map: comp => - applyRewrittenScope(ctx.rewrittenScopes(comp.isym)) match - case c: ClsLikeBody => c - case _ => die + val newComp = c.companion.map(comp => applyRewrittenScope(ctx.rewrittenScopes(comp.isym))) match + case Some(c: ClsLikeBody) => S(c) + case Some(_) => die + case None => N + k(newCls.copy(companion = newComp)) case _ => super.applyDefn(defn)(k) @@ -618,11 +640,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): sealed abstract class RewrittenScope[T](val obj: TScopedObject[T]): val node = obj.node.get - protected val thisCapturedLocals = usedVars.reqdCaptures(obj.toInfo) + protected final val thisCapturedLocals = usedVars.reqdCaptures(obj.toInfo) val hasCapture = !thisCapturedLocals.isEmpty // These are lazy, because we don't necessarily need a captrue - private lazy val captureInfo: (ClsLikeDefn, List[(Local, TermSymbol)]) = createCaptureCls(obj) + private final lazy val captureInfo: (ClsLikeDefn, List[(Local, TermSymbol)]) = createCaptureCls(obj) lazy val captureClass = captureInfo._1 lazy val captureMap = captureInfo._2.toMap @@ -631,7 +653,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): protected def rewriteImpl: LifterResult[T] - protected def addCaptureSym(b: Block, captureSym: Local, define: Bool): Block = + protected final def addCaptureSym(b: Block, captureSym: Local, define: Bool): Block = if hasCapture then val undef = Value.Lit(Tree.UnitLit(false)).asArg val inst = Instantiate( @@ -655,7 +677,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): * * @return The rewritten scoped object, plus any extra scoped definitions arising from lifting the nested scoped objects. */ - def rewrite = + final def rewrite = if hasCapture then val LifterResult(defn, extra) = rewriteImpl LifterResult(defn, captureClass :: extra) @@ -664,9 +686,13 @@ class Lifter(topLevelBlk: Block)(using State, Raise): /** The path to access locals defined by this object. The primary purpose of this is to rewrite accesses * to locals that have been moved to a capture. */ - protected def pathsFromThisObj: Map[Local, LocalPath] = + protected final def pathsFromThisObj: Map[Local, LocalPath] = + // Remove child BlockMemberSymbols; we will use their definition symbols instead + val childrenBms = node.children.collect: + case ScopeNode(obj = r: ScopedObject.Referencable[?]) => r.bsym + // Locals introduced by this object - val fromThisObj = data.getNode(obj.toInfo).localsWithoutLifted + val fromThisObj = node.localsWithoutBms .map: s => s -> s.asLocalPath .toMap @@ -676,20 +702,25 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val tSym = captureMap(s) s -> LocalPath.InCapture(capturePath, tSym) .toMap - // BMS refs from ignored defns - // Note that we map the DefinitionSymbol to the disambiguated BMS. - val fromIgnored = node.children.collect: - case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => - r.sym -> LocalPath.BmsRef(r.bsym, r.sym) // Note: the order here is important, as fromCap must override keys from // fromThisObj. - fromThisObj ++ fromCap ++ fromIgnored + fromThisObj ++ fromCap lazy val capturePaths = if thisCapturedLocals.isEmpty then Map.empty else Map(obj.toInfo -> capturePath) - lazy val symbolsMap: Map[Local, LocalPath] = pathsFromThisObj.toMap + // BMS refs from ignored defns + // Note that we map the DefinitionSymbol to the disambiguated BMS. + protected lazy val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = + node.children.collect: + case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => + r.sym -> DefnRef.InScope(r.bsym, r.sym) + .toMap + + lazy val defnPaths: Map[DefinitionSymbol[?], DefnRef] = defnPathsFromThisObj + + lazy val symbolsMap: Map[Local, LocalPath] = pathsFromThisObj /** Represents a scoped object that is to be rewritten and lifted. */ sealed abstract class LiftedScope[T <: Defn](override val obj: ScopedObject.Liftable[T])(using ctx: LifterCtxNew) extends RewrittenScope[T](obj): @@ -699,10 +730,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise): .toSet /** Symbols that this object will lose access to once lifted, and therefore must receive - * as a parameter. Includes neighbouring objects that this definition may lose access to - * once lifted. + * as a parameter. Does not include neighbouring objects that this definition may lose + * access to. Those are in a separate list. */ - val reqSymbols = accessed ++ node.reqCaptureObjs.map(_.sym).toSet.intersect(refdDSyms) + final val reqSymbols = accessed private val (reqPassedSymbols, captures) = reqSymbols .partitionMap: s => @@ -711,21 +742,29 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case None => L(s) /** Locals that are directly passed to this object, i.e. not via a capture. */ - val passedSyms: Set[Local] = reqPassedSymbols + final val passedSyms: Set[Local] = reqPassedSymbols /** Maps locals to the scope where they were defined. */ - val capturesOrigin: Map[Local, ScopedInfo] = captures.toMap + final val capturesOrigin: Map[Local, ScopedInfo] = captures.toMap /** Locals that are inside captures. */ - val inCaptureSyms: Set[Local] = captures.map(_._1) + final val inCaptureSyms: Set[Local] = captures.map(_._1) /** Scopes whose captures this object requires. */ - val reqCaptures: Set[ScopedInfo] = captures.map(_._2) + final val reqCaptures: Set[ScopedInfo] = captures.map(_._2) + /** + * Neighbouring objects that this definition may lose access to + * once lifted, referenced by their *definition symbol* (not BMS). + */ + final val reqDefns = node.reqCaptureObjs.map(_.sym).toSet.intersect(refdDSyms) /** Maps directly passed locals to the path representing that local within this object. */ protected val passedSymsMap: Map[Local, LocalPath] - /** Maps scopes to the path to the path representing their captures within this object. */ + /** Maps scopes to the path representing their captures within this object. */ protected val capSymsMap: Map[ScopedInfo, Path] + /** Maps definition symbols to the path representing that definition. */ + protected val passedDefnsMap: Map[DefinitionSymbol[?], DefnRef] protected lazy val capturesOrdered: List[ScopedInfo] - protected lazy val passedSymsOrdered: List[Local] + protected final lazy val passedSymsOrdered: List[Local] = reqPassedSymbols.toList.sortBy(_.uid) + protected final lazy val passedDefnsOrdered: List[DefinitionSymbol[?]] = reqDefns.toList.sortBy(_.uid) override lazy val capturePaths = if thisCapturedLocals.isEmpty then capSymsMap @@ -753,10 +792,20 @@ class Lifter(topLevelBlk: Block)(using State, Raise): .toMap fromParents ++ pathsFromThisObj - def formatArgs(captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): List[Arg] = - val captureArgs = capturesOrdered.map(c => captures(c).asArg) - val localArgs = passedSymsOrdered.map(l => locals(l).asArg) - captureArgs ::: localArgs + override lazy val defnPaths: Map[DefinitionSymbol[?], DefnRef] = + val fromParents = reqDefns + .map: s => + s -> passedDefnsMap(s) + .toMap + defnPathsFromThisObj ++ fromParents + + final def formatArgs: List[Arg] = + val defnsArgs = passedDefnsOrdered.map(d => ctx.defnsMap(d).asArg) + val captureArgs = capturesOrdered.map(c => ctx.capturesMap(c).asArg) + val localArgs = passedSymsOrdered.map(l => ctx.symbolsMap(l).asArg) + defnsArgs ::: captureArgs ::: localArgs + + /* MIXINS */ /** * A rewritten scope with a generic VarSymbol capture symbol. @@ -767,12 +816,22 @@ class Lifter(topLevelBlk: Block)(using State, Raise): protected def addCaptureSym(b: Block): Block = addCaptureSym(b, captureSym, true) + sealed trait ClsLikeRewrittenScope[T](isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol) extends RewrittenScope[T]: + // always select using `this` + override lazy val defnPathsFromThisObj = + node.children.collect: + case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => + r.sym -> DefnRef.Field(isym, r.bsym, r.sym) + .toMap + // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) private def dupParamList(plist: ParamList): ParamList = plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) + /* CONCRETE IMPLS */ + class RewrittenScopedBlock(override val obj: ScopedObject.ScopedBlock)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: override def rewriteImpl: LifterResult[Block] = val rewriter = new BlockRewriter @@ -806,7 +865,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): .map: c => ctx.rewrittenScopes(c.obj.toInfo) .collect: - case r: RewrittenFunc if r.obj.isMethod => r + case r: RewrittenFunc if r.obj.isMethod.isDefined => r val (liftedMtds, extras) = mtds.map(liftNestedScopes).unzip(using l => (l.liftedDefn, l.extraDefns)) LifterResult(liftedMtds, extras.flatten) @@ -818,7 +877,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise): def getRewrittenCls = ctx.rewrittenScopes(obj.cls.isym) - class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends RewrittenScope[ClsLikeDefn](obj): + class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) + extends RewrittenScope[ClsLikeDefn](obj) + with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -829,14 +890,34 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) + val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( ctor = ctorWithCap, preCtor = rewrittenPrector, privateFields = captureSym :: obj.cls.privateFields, - methods = newMtds + methods = newMtds, ) LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) + + class RewrittenCompanion(override val obj: ScopedObject.Companion)(using ctx: LifterCtxNew) + extends RewrittenScope[ClsLikeBody](obj) + with ClsLikeRewrittenScope[ClsLikeBody](obj.comp.isym): + + private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.comp.isym), Tree.Ident(obj.nme + "$cap")) + override lazy val capturePath: Path = captureSym.asPath + + override def rewriteImpl: LifterResult[ClsLikeBody] = + val rewriterCtor = new BlockRewriter + val rewrittenCtor = rewriterCtor.rewrite(obj.comp.ctor) + val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) + val LifterResult(newMtds, extras) = rewriteMethods(node, obj.comp.methods) + val newComp = obj.comp.copy( + ctor = ctorWithCap, + privateFields = captureSym :: obj.comp.privateFields, + methods = newMtds + ) + LifterResult(newComp, rewriterCtor.extraDefns.toList ::: extras) class LiftedFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends LiftedScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: private val passedSymsMap_ : Map[Local, VarSymbol] = passedSyms.map: s => @@ -846,15 +927,19 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val nme = data.getNode(i).obj.nme i -> VarSymbol(Tree.Ident(nme + "$cap")) .toMap + private val defnSymsMap_ : Map[DefinitionSymbol[?], VarSymbol] = reqDefns.map: i => + val nme = data.getNode(i).obj.nme + i -> VarSymbol(Tree.Ident(nme + "$")) + .toMap override lazy val capturesOrdered: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).uid) - override lazy val passedSymsOrdered: List[Local] = passedSyms.toList.sortBy(_.uid) override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.asPath).toMap + override protected val passedDefnsMap = defnSymsMap_.view.mapValues(_.asDefnRef).toMap val auxParams: List[Param] = - (capturesOrdered.map(capSymsMap_) ::: passedSymsOrdered.map(passedSymsMap_)) + (passedDefnsOrdered.map(defnSymsMap_) ::: capturesOrdered.map(capSymsMap_) ::: passedSymsOrdered.map(passedSymsMap_)) .map: s => val decl = Param(FldFlags.empty.copy(isVal = false), s, N, Modulefulness.none) s.decl = S(decl) @@ -915,22 +1000,22 @@ class Lifter(topLevelBlk: Block)(using State, Raise): bod )(false) - def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = + def rewriteCall(c: Call)(using ctx: LifterCtxNew): Call = if isTrivial then c else Call( Value.Ref(mainSym, S(mainDsym)), - formatArgs(captures, locals) ::: c.args + formatArgs ::: c.args )( isMlsFun = true, mayRaiseEffects = c.mayRaiseEffects, explicitTailCall = c.explicitTailCall ) - def rewriteRef(captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = + def rewriteRef(using ctx: LifterCtxNew): Call = Call( Value.Ref(auxSym, S(auxDsym)), - formatArgs(captures, locals) + formatArgs )( isMlsFun = true, mayRaiseEffects = false, @@ -941,7 +1026,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val LifterResult(lifted, extra) = mkFlattenedDefn if isTrivial then LifterResult(lifted, extra) else LifterResult(lifted, mkAuxDefn :: extra) - class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) extends LiftedScope[ClsLikeDefn](obj): + class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) + extends LiftedScope[ClsLikeDefn](obj) + with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -961,15 +1048,22 @@ class Lifter(topLevelBlk: Block)(using State, Raise): TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(nme)) ) .toMap + private val defnSymsMap_ : Map[DefinitionSymbol[?], (vs: VarSymbol, ts: TermSymbol)] = reqDefns.map: i => + i -> + ( + VarSymbol(Tree.Ident(i.nme + "$")), + TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(i.nme + "$")) + ) + .toMap override lazy val capturesOrdered: List[ScopedInfo] = reqCaptures.toList.sortBy(c => capSymsMap_(c).vs.uid) - override lazy val passedSymsOrdered: List[Local] = passedSyms.toList.sortBy(_.uid) override protected val passedSymsMap = passedSymsMap_.view.mapValues(_.ts.asLocalPath).toMap override protected val capSymsMap = capSymsMap_.view.mapValues(_.ts.asPath).toMap + override protected val passedDefnsMap = defnSymsMap_.view.mapValues(_.ts.asDefnRef).toMap val auxParams: List[Param] = - (capturesOrdered.map(x => capSymsMap_(x).vs) ::: passedSymsOrdered.map(x => passedSymsMap_(x).vs)) + (passedDefnsOrdered.map(x => defnSymsMap_(x).vs) ::: capturesOrdered.map(x => capSymsMap_(x).vs) ::: passedSymsOrdered.map(x => passedSymsMap_(x).vs)) .map(Param.simple(_)) // Whether this can be lifted without the need to pass extra parameters. @@ -977,21 +1071,21 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val cls = obj.cls - def rewriteInstantiate(inst: Instantiate, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Instantiate = + def rewriteInstantiate(inst: Instantiate): Instantiate = if isTrivial then inst else Instantiate( inst.mut, Value.Ref(cls.sym, S(cls.isym)), - formatArgs(captures, locals) ::: inst.args + formatArgs ::: inst.args ) - def rewriteCall(c: Call, captures: Map[ScopedInfo, Path], locals: Map[Local, LocalPath]): Call = + def rewriteCall(c: Call)(using ctx: LifterCtxNew): Call = if isTrivial then c else Call( Value.Ref(cls.sym, S(cls.ctorSym.get)), - formatArgs(captures, locals) ::: c.args + formatArgs ::: c.args )( isMlsFun = true, mayRaiseEffects = c.mayRaiseEffects, @@ -1045,7 +1139,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case o: ScopedObject.Class => if s.isLifted && !s.isTopLevel then LiftedClass(o) else RewrittenClass(o) - case o: ScopedObject.Companion => ??? + case o: ScopedObject.Companion => RewrittenCompanion(o) case o: ScopedObject.ClassCtor => RewrittenClassCtor(o) case o: ScopedObject.Func => if s.isLifted && !s.isTopLevel then LiftedFunc(o) @@ -1074,7 +1168,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // Note: this will be reset to the original value in liftNestedScopes ctx.symbolsMap ++= scope.symbolsMap ctx.capturesMap ++= scope.capturePaths - + ctx.defnsMap ++= scope.defnPaths + val rewrittenScopes = node.children.map(createRewritten) // The scopes in `lifted` will be rewritten right now // The scopes in `ignored` will be rewritten in-place when traversing the block @@ -1095,9 +1190,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): def liftNestedScopes[T](r: RewrittenScope[T])(using ctx: LifterCtxNew): LifterResult[T] = val curSyms = ctx.symbolsMap val curCaptures = ctx.capturesMap + val curDefns = ctx.defnsMap val ret = liftNestedScopesImpl(r) ctx.symbolsMap = curSyms ctx.capturesMap = curCaptures + ctx.defnsMap = curDefns ret def transform = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index 2f4b922ac0..e82ad00f5d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -13,6 +13,12 @@ import hkmc2.codegen.llir.FreshInt import java.util.IdentityHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet +import hkmc2.ScopeData.ScopedObject.Top +import hkmc2.ScopeData.ScopedObject.Companion +import hkmc2.ScopeData.ScopedObject.ClassCtor +import hkmc2.ScopeData.ScopedObject.Func +import hkmc2.ScopeData.ScopedObject.Loop +import hkmc2.ScopeData.ScopedObject.ScopedBlock object ScopeData: opaque type ScopeUID = BigInt @@ -62,9 +68,9 @@ object ScopeData: // Locals defined by a scoped object. lazy val definedLocals: Set[Local] = this match - case Top(b) => b match - case Scoped(syms, _) => syms.toSet - case _ => Set.empty + // we want definedLocals for the top level scope to be empty, because otherwise, + // the lifter may try to capture those locals. + case Top(b) => Set.empty case Class(cls) => // Public fields are not included, as they are accessed using // a field selection rather than directly using the BlockMemberSymbol. @@ -99,11 +105,12 @@ object ScopeData: case Class(cls) => cls.isym case Companion(comp, par) => comp.isym case Func(fun, isMethod) => fun.dSym + case ClassCtor(cls) => cls.ctorSym.get def bsym: BlockMemberSymbol = this match case Class(cls) => cls.sym case Companion(comp, par) => par.sym case Func(fun, isMethod) => fun.sym - + case ClassCtor(cls) => cls.sym // Scoped nodes which could possibly be lifted to the top level. sealed abstract class Liftable[T <: Defn] extends Referencable[T]: @@ -115,8 +122,12 @@ object ScopeData: val defn = cls case class Companion(comp: ClsLikeBody, par: ClsLikeDefn) extends Referencable[ClsLikeBody] // We model it like this: the ctor is just another function in the same scope as the class and initializes the corresponding class - case class ClassCtor(cls: ClsLikeDefn) extends ScopedObject[Unit] - case class Func(fun: FunDefn, isMethod: Bool) extends Liftable[FunDefn]: + case class ClassCtor(cls: ClsLikeDefn) extends Referencable[Unit] + // isMethod: + // N = not a method + // S(false) = module method + // S(true) = class or object method + case class Func(fun: FunDefn, isMethod: Opt[Bool]) extends Liftable[FunDefn]: val defn = fun // The purpose of `Loop` is to enforce the rule that the control flow remains linear when we enter // a scoped block. @@ -179,7 +190,28 @@ object ScopeData: case Some(value) => value.existingVars ++ value.obj.definedLocals case None => Set.empty - // The following must not be called until ignoredScopes is populated with the relevant data. + lazy val isTopLevel: Bool = parent match + case Some(ScopeNode(obj = _: ScopedObject.Top)) => true + case _ => false + + lazy val isInTopLevelMod: Bool = parent match + case Some(par) => par.obj match + case _: Companion => par.isInTopLevelMod + case s: ScopedBlock => s.node.get.parent.get.obj match + case c: Companion => c.node.get.parent.get.isInTopLevelMod + case _ => false + case _ => false + case None => true + + // Scoped blocks include the BlockMemberSymbols of their nested definitions. This removes them. + lazy val localsWithoutBms: Set[Local] = obj match + case s: ScopedObject.ScopedBlock => + val rmv = children.collect: + case c @ ScopeNode(obj = s: ScopedObject.Referencable[?]) => s.bsym + obj.definedLocals -- rmv + case _ => obj.definedLocals + + // All of the following must not be called until ignoredScopes is populated with the relevant data. lazy val isLifted: Bool = val ignored = ignoredScopes.ignored match @@ -193,14 +225,11 @@ object ScopeData: // case _: ScopedObject.Companion => false // case c: ScopedObject.Class if c.cls.companion.isDefined => false case _ if ignored.contains(obj.toInfo) => false - case ScopedObject.Func(isMethod = true) => false + case _ if isInTopLevelMod => false + case ScopedObject.Func(isMethod = S(true)) => false case _: ScopedObject.Loop | _: ScopedObject.ClassCtor | _: ScopedObject.ScopedBlock | _: ScopedObject.Companion => false case _ => true - lazy val isTopLevel: Bool = parent match - case Some(ScopeNode(obj = _: ScopedObject.Top)) => true - case _ => false - lazy val liftedChildNodes: List[ScopeNode[?]] = if isLifted then this :: Nil else children.flatMap(_.liftedChildNodes) @@ -228,16 +257,7 @@ object ScopeData: case _ => if isLifted then reqCaptureObjsImpl else parent.get.reqCaptureObjsImpl - - // Scoped blocks include the BlockMemberSymbols of their nested definitions. This removes the ones - // belonging to objects that are lifted. - lazy val localsWithoutLifted: Set[Local] = obj match - case s: ScopedObject.ScopedBlock => - val rmv = children.collect: - case c @ ScopeNode(obj = s: ScopedObject.Liftable[?]) if c.isLifted => s.defn.sym - obj.definedLocals -- rmv - case _ => obj.definedLocals - + def dSymUnapply(data: ScopeData, v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = v match case Some(d) if data.contains(d) => S(d) case d: DefinitionSymbol[?] if data.contains(d) => S(d) @@ -250,6 +270,8 @@ class ScopeData(b: Block)(using State, IgnoredScopes): val scopeTree = NestedScopeTree(makeScopeTreeRec(ScopedObject.Top(b))) val root = scopeTree.root + val allBms = root.allChildren.collect: + case s: ScopedObject.Referencable[?] => s.bsym def contains(s: ScopedInfo) = scopeTree.nodesMap.contains(s) @@ -277,7 +299,7 @@ class ScopeData(b: Block)(using State, IgnoredScopes): objs ::= ScopedObject.Loop(l.label, l.body) case _ => super.applyBlock(b) override def applyFunDefn(fun: FunDefn): Unit = - objs ::= ScopedObject.Func(fun, false) + objs ::= ScopedObject.Func(fun, N) override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => applyFunDefn(f) case c: ClsLikeDefn => @@ -310,8 +332,8 @@ class ScopeData(b: Block)(using State, IgnoredScopes): case ScopedObject.ClassCtor(c) => () case ScopedObject.Loop(_, b) => finder.applyBlock(b) val mtdObjs = obj match - case ScopedObject.Class(cls) => cls.methods.map(ScopedObject.Func(_, true)) - case ScopedObject.Companion(comp, par) => comp.methods.map(ScopedObject.Func(_, true)) + case ScopedObject.Class(cls) => cls.methods.map(ScopedObject.Func(_, S(true))) + case ScopedObject.Companion(comp, par) => comp.methods.map(ScopedObject.Func(_, S(false))) case _ => Nil val children = (mtdObjs ::: finder.objs).map(makeScopeTreeRec) val retNode = ScopeNode.ScopeNode(obj, N, children) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 600572690c..9bd6dd71a3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -82,9 +82,9 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // If so, then it requires reading the class symbol val node = scopeData.getNode(dSym) node.obj match - case Func(isMethod = false) => accessed.refdDefns.add(node.obj.toInfo) - case f @ Func(isMethod = true) => accessed.accessed.add(f.fun.owner.get) - case _: ScopedObject.Class | _: ClassCtor => accessed.refdDefns.add(node.obj.toInfo) + case f @ Func(isMethod = S(true)) => accessed.accessed.add(f.fun.owner.get) + case Func(isMethod = N | S(false)) => accessed.refdDefns.add(node.obj.toInfo) + case _: ScopedObject.Class | _: ClassCtor | _: Companion => accessed.refdDefns.add(node.obj.toInfo) case _ => () case Value.Ref(l, _) => accessed.accessed.add(l) @@ -377,13 +377,18 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes def handleCalledScope(called: ScopedInfo): Unit = scopeInfos.get(called) match case None => () case Some(node) => + node.obj match + // ignore method calls to class or object methods + case ScopedObject.Func(_, S(true)) => return + case _ => () + val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(called) val muts = muted.intersect(thisVars) val reads = accessed.intersect(thisVars) -- muts val refdExcl = refd.filter: sym => scopeData.getNode(sym).obj match case s: ScopedObject.ScopedBlock => false - case ScopedObject.Func(_, true) => false + case ScopedObject.Func(_, S(true)) => false case _ => true // This not a naked reference. If it's a ref to a class, this can only ever create once instance @@ -458,7 +463,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes override def applyDefn(defn: Defn): Unit = defn match case c: ClsLikeDefn if modOrObj(c) => - handleCalledScope(c.isym) + handleCalledScope(c.isym) // TODO: use new system super.applyDefn(defn) case _ => super.applyDefn(defn) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 8b8d516d49..ab71773209 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -125,6 +125,25 @@ h3.perform() //│ } in //│ define class Handler$h3$ { //│ let Handler$h3$$cap = ... +//│ preCtor { +//│ set tmp4 = super() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp4 = runtime.illegalEffect("in a constructor") in +//│ end +//│ in +//│ return tmp4 +//│ } +//│ ctor { +//│ set h23 = h22 in +//│ end +//│ } +//│ fun class:Handler$h3$‹718›::perform() { +//│ set Handler$h3$perform$here = Handler$h3$perform$(h2) in +//│ return runtime.mkEffect(Handler$h3$, Handler$h3$perform$here) +//│ } //│ } in //│ define fun handleBlock$(h31) { //│ match runtime.resumePc @@ -148,7 +167,7 @@ h3.perform() //│ set pc2 = 1 in //│ continue main2 //│ 1 => -//│ set tmp4 = runtime.resumeValue in +//│ set tmp5 = runtime.resumeValue in //│ return h31.perform() //│ else //│ @@ -156,13 +175,27 @@ h3.perform() //│ end in //│ end //│ } in -//│ define fun handleBlock$$(h23)() { -//│ return handleBlock$(h23) +//│ define fun handleBlock$$(h24)() { +//│ return handleBlock$(h24) //│ } in //│ define class Handler$h2$ { //│ let Handler$h2$$cap = ... +//│ preCtor { +//│ set tmp6 = super() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp6 = runtime.illegalEffect("in a constructor") in +//│ end +//│ in +//│ return tmp6 +//│ } +//│ fun class:Handler$h2$‹706›::perform() { +//│ return runtime.mkEffect(Handler$h2$, Handler$h2$perform) +//│ } //│ } in -//│ define fun handleBlock$(h24) { +//│ define fun handleBlock$(h25) { //│ match runtime.resumePc //│ -1 => //│ set pc3 = 0 in @@ -174,20 +207,20 @@ h3.perform() //│ in //│ labelled loop main3 = match pc3 //│ 0 => -//│ set h32 = new mut Handler$h3$(h24) in +//│ set h32 = new mut Handler$h3$(h25) in //│ set handleBlock$$here = handleBlock$$(h32) in //│ set runtime.resumeValue = runtime.enterHandleBlock(h32, handleBlock$$here) in //│ match runtime.curEffect //│ null => //│ end //│ else -//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 1, h24, 0) +//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 1, h25, 0) //│ in //│ set pc3 = 1 in //│ continue main3 //│ 1 => -//│ set tmp5 = runtime.resumeValue in -//│ return tmp5 +//│ set tmp7 = runtime.resumeValue in +//│ return tmp7 //│ else //│ //│ in @@ -216,7 +249,7 @@ h3.perform() //│ set pc4 = 1 in //│ continue main4 //│ 1 => -//│ set tmp6 = runtime.resumeValue in +//│ set tmp8 = runtime.resumeValue in //│ return k3() //│ else //│ @@ -226,6 +259,20 @@ h3.perform() //│ } in //│ define class Handler$h1$ { //│ let Handler$h1$$cap = ... +//│ preCtor { +//│ set tmp9 = super() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp9 = runtime.illegalEffect("in a constructor") in +//│ end +//│ in +//│ return tmp9 +//│ } +//│ fun class:Handler$h1$‹695›::perform() { +//│ return runtime.mkEffect(Handler$h1$, Handler$h1$perform) +//│ } //│ } in //│ set h1 = new mut Handler$h1$() in //│ define fun handleBlock$() { @@ -240,9 +287,9 @@ h3.perform() //│ in //│ labelled loop main5 = match pc5 //│ 0 => -//│ set h25 = new mut Handler$h2$() in -//│ set handleBlock$$here1 = handleBlock$$(h25) in -//│ set runtime.resumeValue = runtime.enterHandleBlock(h25, handleBlock$$here1) in +//│ set h26 = new mut Handler$h2$() in +//│ set handleBlock$$here1 = handleBlock$$(h26) in +//│ set runtime.resumeValue = runtime.enterHandleBlock(h26, handleBlock$$here1) in //│ match runtime.curEffect //│ null => //│ end @@ -252,23 +299,23 @@ h3.perform() //│ set pc5 = 1 in //│ continue main5 //│ 1 => -//│ set tmp7 = runtime.resumeValue in -//│ return tmp7 +//│ set tmp10 = runtime.resumeValue in +//│ return tmp10 //│ else //│ //│ in //│ end in //│ end //│ } in -//│ set tmp8 = runtime.enterHandleBlock(h1, handleBlock$) in +//│ set tmp11 = runtime.enterHandleBlock(h1, handleBlock$) in //│ match runtime.curEffect //│ null => //│ end //│ else -//│ set tmp8 = runtime.topLevelEffect(false) in +//│ set tmp11 = runtime.topLevelEffect(false) in //│ end //│ in -//│ set block$res2 = tmp8 in +//│ set block$res2 = tmp11 in //│ return undefined //│ > h1 //│ > h3 diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 71d8d4773b..6809d109b0 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,140 +9,42 @@ :todo :noSanityCheck -class Eff -//│ Elab: { Cls Eff { }; } - :lift -:sjs +:ssjs fun f(x) = - while false do - let i = 0 - set x += 1 - set i += 1 - fun g() = x + i - g -//│ Elab: { fun member:f‹692›(x‹693›) = while { let scrut = false; $scrut‹695›#0 is true then { let i‹696›; i‹696› = 0; x‹693›#666 := builtin:+‹48›#0(x‹693›#666, 1); i‹696›#666 := builtin:+‹48›#1(i‹696›#666, 1); fun member:g‹691›() = builtin:+‹48›#2(x‹693›#666, i‹696›#666); member:g‹691›#666 }; else () }; } -//│ JS (unsanitized): -//│ let g, f, Capture$f1, g$; -//│ g$ = function g$(f$cap, i) { + class A with + fun get = x + module A + fun g = new A +//│ Elab: { fun member:f‹873›(x‹874›) = { Cls A { method fun member:get‹870› = x‹874›#666; }; Mod A { }; fun member:g‹872› = new member:A‹871›#666; () }; } +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.17: module A +//│ ╙── ^ +//│ JS: +//│ let g, f, g$; +//│ g$ = function g$(A$) { //│ return () => { -//│ return g(f$cap, i) +//│ return g(A$) //│ } //│ }; -//│ g = function g(f$cap, i) { -//│ return f$cap.x$0 + i +//│ g = function g(A$) { +//│ return globalThis.Object.freeze(new A$()) //│ }; -//│ (class Capture$f { -//│ static { -//│ Capture$f1 = this -//│ } -//│ constructor(x$0) { -//│ this.x$0 = x$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Capture$f"]; -//│ }); //│ f = function f(x) { -//│ let tmp, f$cap; -//│ f$cap = new Capture$f1(x); -//│ lbl: while (true) { -//│ let scrut, i, tmp1, tmp2, g$here; -//│ scrut = false; -//│ if (scrut === true) { -//│ i = 0; -//│ tmp1 = f$cap.x$0 + 1; -//│ f$cap.x$0 = tmp1; -//│ tmp2 = i + 1; -//│ i = tmp2; -//│ g$here = g$(f$cap, i); -//│ tmp = g$here; -//│ continue lbl -//│ } else { tmp = runtime.Unit; } -//│ break; -//│ } -//│ return tmp -//│ }; - -:slot -class C with - let x = 2 - fun f(x) = 2 -//│ Elab: { Cls C { let term:class:C‹755›.x‹757›; term:class:C‹755›.x‹757› = 2; method fun member:f‹753›(x‹758›) = 2; }; } -//│ Pretty Lowered: -//│ -//│ define class C { -//│ let x = ... -//│ ctor { -//│ set x = 2 in -//│ end -//│ } -//│ fun class:C‹755›::f(x1) { -//│ return 2 -//│ } -//│ } in -//│ set block$res3 = undefined in -//│ end - -:slot -module M with - fun bruh = 2 - fun lol = 54 -//│ Elab: { Mod M { fun member:bruh‹765› = 2; fun member:lol‹764› = 54; }; } -//│ Pretty Lowered: -//│ -//│ define class M in -//│ module M { -//│ fun module:M‹767›::bruh { -//│ return 2 -//│ } -//│ fun module:M‹767›::lol { -//│ return 54 -//│ } -//│ } in -//│ set block$res4 = undefined in -//│ end - -:slot -class A(x) with - fun bruh = 2 - fun a = 4 -//│ Elab: { Cls AParamList(‹›,List(Param(‹›,x‹784›,None,Modulefulness(None))),None) { let term:class:A‹782›.x‹787›; term:class:A‹782›.x‹787› = x‹784›#0; method fun member:bruh‹780› = 2; method fun member:a‹779› = 4; }; } -//│ Pretty Lowered: -//│ -//│ define class A(x) { -//│ let x = ... -//│ ctor { -//│ set x1 = x in -//│ end -//│ } -//│ fun class:A‹782›::bruh { -//│ return 2 -//│ } -//│ fun class:A‹782›::a { -//│ return 4 -//│ } -//│ } in -//│ set block$res5 = undefined in -//│ end - -:slot -fun g = 1 -fun h = 4 -fun f(x) = - set x += 2 -//│ Elab: { fun member:g‹797› = 1; fun member:h‹798› = 4; fun member:f‹796›(x‹805›) = x‹805›#666 := builtin:+‹48›#3(x‹805›#666, 2); } -//│ Pretty Lowered: -//│ -//│ define fun g() { -//│ return 1 -//│ } in -//│ define fun h() { -//│ return 4 -//│ } in -//│ define fun f(x) { -//│ set tmp = +(x, 2) in -//│ set x = tmp in +//│ let A1; +//│ (class A { +//│ static { +//│ A1 = this +//│ } +//│ constructor() {} +//│ static #A_mod$cap; +//│ #A$cap; +//│ get get() { +//│ return x; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); //│ return runtime.Unit -//│ } in -//│ set block$res6 = undefined in -//│ end +//│ }; +//│ block$res1 = undefined; diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 68e95d2665..0bd3ec0730 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -82,8 +82,8 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[RUNTIME ERROR] Expected: '1', got: '2' -//│ = 2 +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. +//│ = 1 :todo @@ -99,12 +99,14 @@ fun foo1(x, n) = fun foo2() = class C() C +//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :w fun foo3() = class C() fun f = C() C +//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :todo :expect "NN" @@ -140,10 +142,7 @@ module M with fun g() = x g M.f()() -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.137: module M with -//│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ = 2 /// The following are related to modules and objects. /// @@ -156,28 +155,12 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.152: module M with +//│ ║ l.157: module M with //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing :expect 14 foo(10, 0) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.164: foo(10, 0) -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.151: fun foo(x, y) = -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.152: module M with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.153: val test = 2 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.154: fun foo() = -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.155: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' +//│ = 14 fun foo(x, y) = module M with @@ -187,27 +170,24 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.183: module M with +//│ ║ l.172: module M with //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing - -:expect 12 -foo(10, 0) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.195: foo(10, 0) -//│ ║ ^^^ +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' +//│ ║ l.176: fun foo = M.foo() +//│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.182: fun foo(x, y) = -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.183: module M with +//│ ║ l.172: module M with +//│ ║ ^^^^^^ +//│ ║ l.173: fun foo() = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.184: fun foo() = +//│ ║ l.174: set y = 2 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.185: set y = 2 -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.186: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ ║ l.175: x + y +//│ ╙── ^^^^^^^^^^^ + +:expect 12 +foo(10, 0) +//│ ═══[RUNTIME ERROR] ReferenceError: M is not defined //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -215,24 +195,13 @@ data class A(x) with module M with fun getB() = x fun getA() = M.getB() -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.201: module M with +//│ ╙── ^ :expect 2 A(2).getA() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.224: A(2).getA() -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.214: data class A(x) with -//│ ║ ^^^^^^^^^ -//│ ║ l.215: module M with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.216: fun getB() = x -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.217: fun getA() = M.getB() -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 // TODO: Foo needs to be put in a mutable capture. Also, we need to pass the Foo instance itself into Foo fun foo(x) = @@ -243,9 +212,10 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.245: class Bar extends Foo +//│ ║ l.217: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. +//│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. //│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. //│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null @@ -259,8 +229,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.259: module M with +//│ ║ l.232: module M with //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index 2388e2b86a..174af5807e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -157,61 +157,33 @@ module Test with j / i fun main()(using Debugger) = test(12) + test(34) -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1048) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1109) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1108) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // Currently this test fails due to lifter issue. Commenting out :lift at the top of this file will make this work. // The lifter currently does not correctly consider the member variable as a local that could be captured. -:fixme let res = handle h = Debugger with fun break(payload)(resume) = resume using Debugger = h Runtime.try(() => Test.main()) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'Test' -//│ ║ l.180: Runtime.try(() => Test.main()) -//│ ║ ^^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.151: module Test with -//│ ║ ^^^^^^^^^ -//│ ║ l.152: fun test(j)(using dbg: Debugger) = -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.153: let i = 0 -//│ ║ ^^^^^^^^^^^^^ -//│ ║ l.154: let k = 2000 -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.155: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: Test is not defined -//│ res = undefined +//│ res = EffectHandle(_) :re res.raise() -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'raise') +//│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ +//│ at Test.test (Debugging.mls:156:18) +//│ at Test.main (pc=3) -:fixme set res = res.resumeWith(42) -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'resumeWith') :re res.raise() -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'raise') +//│ ═══[RUNTIME ERROR] Error: Unhandled effect Handler$h$ +//│ at Test.test (Debugging.mls:156:18) +//│ at Test.main (pc=1) -:fixme :expect 0.33676533676533676 res.resumeWith(666) -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'resumeWith') -//│ ═══[RUNTIME ERROR] Expected: '0.33676533676533676', got: 'undefined' +//│ = 0.33676533676533676 let i = 100 fun f() = @@ -227,8 +199,8 @@ fun f() = f() //│ > Stack Trace: -//│ > at f (Debugging.mls:221:3) with locals: j=200 +//│ > at f (Debugging.mls:193:3) with locals: j=200 //│ > Stack Trace: -//│ > at f (Debugging.mls:223:3) +//│ > at f (Debugging.mls:195:3) //│ > Stack Trace: //│ > at tail position diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 1b91d5e920..4eb93ce2b4 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -277,65 +277,11 @@ module A with data class Effect(x) with fun test() = x fun perform(arg) -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.276: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) handle h = A.Effect(3) with fun perform()(k) = 0 h.perform() -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, $tmp -> tmp2, member:handleBlock$ -> handleBlock$2, $runtime -> runtime, member:Handler$h$perform -> Handler$h$perform2, $definitionMetadata -> definitionMetadata, member:Handler$h$perform$ -> Handler$h$perform$7, $prettyPrint -> prettyPrint, $Term -> Term, member:Handler$h$ -> Handler$h$5, $Block -> Block, $Shape -> Shape, h -> h3, $block$res -> block$res10, class:Handler$h$ -> Handler$h$6, $tmp -> tmp7, member:handleBlock$ -> handleBlock$7, member:Handler$h$perform -> Handler$h$perform7, member:Handler$h$ -> Handler$h$15, $block$res -> block$res5, $tmp -> tmp3, member:handleBlock$ -> handleBlock$3, member:Handler$h$perform$ -> Handler$h$perform$4, member:Handler$h$perform -> Handler$h$perform3, member:Handler$h$ -> Handler$h$7, member:Handler$h$perform$ -> Handler$h$perform$, member:f -> f, h -> h11, class:Handler$h$ -> Handler$h$22, h -> h4, class:Handler$h$ -> Handler$h$8, h -> h8, $block$res -> block$res14, class:Handler$h$ -> Handler$h$16, $tmp -> tmp11, member:handleBlock$ -> handleBlock$11, member:Handler$h$perform -> Handler$h$perform11, member:Handler$h$ -> Handler$h$23, member:Handler$h$perform$ -> Handler$h$perform$8, $block$res -> block$res6, $tmp -> tmp4, member:handleBlock$ -> handleBlock$4, $block$res -> block$res11, member:Handler$h$perform -> Handler$h$perform4, $tmp -> tmp8, member:handleBlock$ -> handleBlock$8, member:Handler$h$ -> Handler$h$9, member:Handler$h$perform -> Handler$h$perform8, member:Handler$h$perform$ -> Handler$h$perform$1, member:Handler$h$ -> Handler$h$17, member:Handler$h$perform$ -> Handler$h$perform$5, member:foo -> foo, h -> h5, class:Handler$h$ -> Handler$h$10, member:foo -> foo2, h -> h12, h -> h9, class:Handler$h$ -> Handler$h$24, $block$res -> block$res7, class:Handler$h$ -> Handler$h$18, $tmp -> tmp5, member:handleBlock$ -> handleBlock$5, member:Handler$h$perform -> Handler$h$perform5, member:Handler$h$ -> Handler$h$11, $block$res -> block$res15, $tmp -> tmp12, member:handleBlock$ -> handleBlock$12, member:Handler$h$perform$ -> Handler$h$perform$2, member:Handler$h$perform -> Handler$h$perform12, member:Handler$h$ -> Handler$h$25, $block$res -> block$res12, $tmp -> tmp9, member:handleBlock$ -> handleBlock$9, member:Handler$h$perform -> Handler$h$perform9, member:foo -> foo1, member:Handler$h$ -> Handler$h$19, member:Handler$h$perform$ -> Handler$h$perform$6, $block$res -> block$res8, h -> h6, class:Handler$h$ -> Handler$h$12, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res9, $tmp -> tmp6, $block$res -> block$res1, member:lambda -> lambda, member:handleBlock$ -> handleBlock$6, h -> h, class:Handler$h$ -> Handler$h$, member:Handler$h$perform -> Handler$h$perform6, member:bar -> bar, member:foobar -> foobar, member:foo -> foo3, member:Handler$h$ -> Handler$h$13, member:Handler$h$perform$ -> Handler$h$perform$3, $block$res -> block$res2, $tmp -> tmp, member:handleBlock$ -> handleBlock$, member:Handler$h$perform -> Handler$h$perform, member:Handler$h$ -> Handler$h$1, member:Predef -> Predef, h -> h1, class:Handler$h$ -> Handler$h$2, h -> h10, class:Handler$h$ -> Handler$h$20, result -> result, h -> h7, class:Handler$h$ -> Handler$h$14, $block$res -> block$res3, $tmp -> tmp1, member:handleBlock$ -> handleBlock$1, member:Handler$h$perform -> Handler$h$perform1, member:Handler$h$ -> Handler$h$3, $block$res -> block$res13, h -> h2, $tmp -> tmp10, class:Handler$h$ -> Handler$h$4, member:handleBlock$ -> handleBlock$10, member:Handler$h$perform -> Handler$h$perform10, member:Handler$h$ -> Handler$h$21) -//│ curThis = S of S of class:Handler$h$ -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.298: handle h = A.Effect(3) with -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.276: module A with -//│ ║ ^^^^^^ -//│ ║ l.277: data class Effect(x) with -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.278: fun test() = x -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.279: fun perform(arg) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ at REPL53:1:282 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ = 0 fun f(perform) = handle h = Effect with @@ -497,18 +443,18 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.492: module A with +//│ ║ l.438: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.488: module A with +//│ ║ l.434: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.484: module A with +//│ ║ l.430: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.480: module A with +//│ ║ l.426: module A with //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. // Access superclass fields handle h1 = Object with diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls index e50396aa3c..ea423aff62 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls @@ -3,23 +3,6 @@ :lift module M -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.5: module M -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // * Notice that the two module definitions are lifted to the top of the block. :sjs @@ -32,11 +15,11 @@ fun foo(h): module M = module A A //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.32: module A +//│ ║ l.15: module A //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.29: module A +//│ ║ l.12: module A //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index cee78a7d5e..ae9508dcc6 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -118,23 +118,6 @@ fun f() = () module A with f() () // defeats tail call optimization -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.118: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) let obj = new Object with fun foo() = print("Hello") diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls index 5278c85c00..bd9ed791c1 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersScratch.mls @@ -2,97 +2,5 @@ :effectHandlers :lift -abstract class Effect -class PrintEffect - -:sjs -// :sjs -handle h = PrintEffect with - fun p(x)(k) = - print(x) - k(()) - fun aux(x)(k) = - h.p(x) - k(()) - h.p(x) -h.aux(3) -//│ FAILURE: Unexpected type error -//│ FAILURE LOCATION: subterm (Elaborator.scala:481) -//│ ╔══[ERROR] Name not found: h -//│ ║ l.17: h.p(x) -//│ ╙── ^ -//│ FAILURE: Unexpected type error -//│ FAILURE LOCATION: subterm (Elaborator.scala:481) -//│ ╔══[ERROR] Name not found: h -//│ ║ l.19: h.p(x) -//│ ╙── ^ -//│ JS (unsanitized): -//│ let h, tmp, handleBlock$, Handler$h$p, Handler$h$aux, Handler$h$1, Handler$h$p$; -//│ Handler$h$aux = function Handler$h$aux(k) { -//│ /* error */ -//│ }; -//│ Handler$h$p$ = function Handler$h$p$(x) { -//│ return (k) => { -//│ return Handler$h$p(x, k) -//│ } -//│ }; -//│ Handler$h$p = function Handler$h$p(x, k) { -//│ let tmp1, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ runtime.resumeValue = Predef.print(x); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h$p, 1, "HandlersScratch.mls:14:5", null, null, 1, 2, x, k, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp1 = runtime.resumeValue; -//│ return runtime.safeCall(k(runtime.Unit)); -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ (class Handler$h$ extends PrintEffect1 { -//│ static { -//│ Handler$h$1 = this -//│ } -//│ constructor() { -//│ let tmp1; -//│ tmp1 = super(); -//│ if (runtime.curEffect !== null) { -//│ tmp1 = runtime.illegalEffect("in a constructor"); -//│ } -//│ tmp1; -//│ } -//│ #Handler$h$$cap; -//│ p(x) { -//│ let Handler$h$p$here; -//│ Handler$h$p$here = Handler$h$p$(x); -//│ return runtime.mkEffect(this, Handler$h$p$here) -//│ } -//│ aux(x) { -//│ return runtime.mkEffect(this, Handler$h$aux) -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Handler$h$"]; -//│ }); -//│ h = new Handler$h$1(); -//│ handleBlock$ = (undefined, function () { -//│ return runtime.safeCall(h.aux(3)) -//│ }); -//│ tmp = runtime.enterHandleBlock(h, handleBlock$); -//│ if (runtime.curEffect !== null) { tmp = runtime.topLevelEffect(false); } -//│ tmp diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 89edec5eca..5add78aec1 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -237,7 +237,18 @@ fun handleEffects(g) = //│ } in //│ define class Handler$h2$ { //│ let Handler$h2$$cap = ... -//│ fun class:Handler$h2$ :: f(x4) { +//│ preCtor { +//│ set tmp14 = super() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp14 = runtime.illegalEffect("in a constructor") in +//│ end +//│ in +//│ return tmp14 +//│ } +//│ fun class:Handler$h2$::f(x4) { //│ set Handler$h2$f$here = Handler$h2$f$(x4) in //│ return runtime.mkEffect(Handler$h2$, Handler$h2$f$here) //│ } @@ -250,7 +261,18 @@ fun handleEffects(g) = //│ } in //│ define class Handler$h1$ { //│ let Handler$h1$$cap = ... -//│ fun class:Handler$h1$ :: f(x5) { +//│ preCtor { +//│ set tmp15 = super() in +//│ match runtime.curEffect +//│ null => +//│ end +//│ else +//│ set tmp15 = runtime.illegalEffect("in a constructor") in +//│ end +//│ in +//│ return tmp15 +//│ } +//│ fun class:Handler$h1$::f(x5) { //│ set Handler$h1$f$here = Handler$h1$f$(x5) in //│ return runtime.mkEffect(Handler$h1$, Handler$h1$f$here) //│ } @@ -279,8 +301,8 @@ fun handleEffects(g) = //│ set pc2 = 1 in //│ continue main2 //│ 1 => -//│ set tmp14 = runtime.resumeValue in -//│ return tmp14 +//│ set tmp16 = runtime.resumeValue in +//│ return tmp16 //│ else //│ //│ in @@ -311,8 +333,8 @@ fun handleEffects(g) = //│ set pc3 = 1 in //│ continue main3 //│ 1 => -//│ set tmp15 = runtime.resumeValue in -//│ return tmp15 +//│ set tmp17 = runtime.resumeValue in +//│ return tmp17 //│ else //│ //│ in diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index ef8c666915..4be34e1871 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -7,7 +7,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -27,7 +27,7 @@ fun foo(x) = let xs = [() => return x + 1] xs.0() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -52,7 +52,7 @@ foo(123) //│ _2 = Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res1, $tmp -> tmp, member:Predef‹726› -> Predef, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res) +//│ bindings = HashMap($block$res -> block$res1, $tmp -> tmp, member:Predef -> Predef, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' //│ ║ l.45: foo(123) //│ ║ ^^^ @@ -92,10 +92,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -124,10 +124,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -147,7 +147,7 @@ f() fun f() = () => return 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -166,7 +166,7 @@ f()() fun f(): () -> Int = () => return () => 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -217,7 +217,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:234) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 8e3e020a25..2131c4ac86 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -17,7 +17,7 @@ fun f() = return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -43,7 +43,7 @@ fun f() = return 3 10 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:233) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 9fa109a3e0..85108f1869 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -175,23 +175,7 @@ module A with else n + f(n-1) val x = f(10000) A.x -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.172: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ = 50005000 :re fun foo(h) = h.perform(2) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 76eb8cfcef..ee49179ad4 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -173,8 +173,8 @@ let b = a.newA() b.foo() a.getX() //│ = 2 -//│ a = A(_) -//│ b = A(_) +//│ a = A(_, _) +//│ b = A(_, _) // handler @@ -196,8 +196,8 @@ fun f() = fun foo() = Test foo()(0) f().get() +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 0 -//│ FAILURE: Unexpected lack of warnings :lift :w @@ -210,23 +210,8 @@ fun f(x) = let foo = Test foo() f(2).get() -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. -//│ at Runtime.checkCall (file:///storage/mark/Repos/mlscript/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs:629:41) -//│ at Test10.get (REPL42:1:748) -//│ at REPL42:1:1115 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' -//│ FAILURE: Unexpected lack of warnings +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. +//│ = 2 :expect 2 fun test() = diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls index 2467c33377..5e3a2396d1 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassWithCompanion.mls @@ -12,57 +12,9 @@ fun foo(x) = //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.9: module C with //│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) -//│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) foo(10).get -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:foo -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $block$res -> block$res1, $Block -> Block, $tmp -> tmp, $Shape -> Shape, $selRes -> selRes, $block$res -> block$res, $discarded -> discarded, member:Predef -> Predef) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.28: foo(10).get -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.6: fun foo(x) = -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.7: class C(y) with -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.8: fun get = [x, y] -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.9: module C with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.10: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ at REPL10:1:48 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ = [10, 123] :w fun foo(x) = @@ -72,58 +24,10 @@ fun foo(x) = val empty = new C C.empty //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.71: module C with +//│ ║ l.23: module C with //│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) -//│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) foo(10).get -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:foo -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res2, $tmp -> tmp1, $runtime -> runtime, $selRes -> selRes1, $definitionMetadata -> definitionMetadata, $discarded -> discarded1, $prettyPrint -> prettyPrint, $Term -> Term, $block$res -> block$res1, $Block -> Block, $tmp -> tmp, $Shape -> Shape, $selRes -> selRes, $block$res -> block$res, $discarded -> discarded, member:Predef -> Predef) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.90: foo(10).get -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.68: fun foo(x) = -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.69: class C with -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.70: fun get = x -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.71: module C with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.72: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ at REPL13:1:52 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ = 10 diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index b9dcff6da1..290bfbf624 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -12,17 +12,35 @@ fun f(x) = //│ ╔══[WARNING] Modules are not yet lifted. //│ ║ l.10: module A //│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.$anonfun$33(Lifter.scala:1077) -//│ at: scala.collection.immutable.List.map(List.scala:236) -//│ at: hkmc2.Lifter.liftNestedScopesImpl(Lifter.scala:1077) -//│ at: hkmc2.Lifter.liftNestedScopes(Lifter.scala:1097) -//│ at: hkmc2.Lifter$ScopeRewriter.applyRewrittenScope(Lifter.scala:572) -//│ at: hkmc2.Lifter$ScopeRewriter.applyBlock(Lifter.scala:579) -//│ at: hkmc2.Lifter$BlockRewriter.applyBlock(Lifter.scala:453) -//│ at: hkmc2.Lifter$BlockRewriter.rewrite(Lifter.scala:298) +//│ JS (unsanitized): +//│ let g, f, g$; +//│ g$ = function g$(A$) { +//│ return () => { +//│ return g(A$) +//│ } +//│ }; +//│ g = function g(A$) { +//│ return globalThis.Object.freeze(new A$()) +//│ }; +//│ f = function f(x) { +//│ let A1; +//│ (class A { +//│ static { +//│ A1 = this +//│ } +//│ constructor() {} +//│ static #A_mod$cap; +//│ #A$cap; +//│ get get() { +//│ return x; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); +//│ return runtime.Unit +//│ }; +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.10: module A +//│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 0049d276f3..43865ff121 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -1,44 +1,24 @@ :js :lift -:slot data class A(x) with data class B(y) with fun getB() = x + y fun getA() = B(2).getB() -//│ Pretty Lowered: -//│ -//│ define class B(A, y) { -//│ let B$cap = ... -//│ y -//│ ctor { -//│ set A1 = A in -//│ define val y = y in -//│ end -//│ } -//│ fun class:B::getB() { -//│ return +(A.x, B.y) -//│ } -//│ } in -//│ define class A(x) { -//│ let A$cap = ... -//│ x -//│ ctor { -//│ define val x = x in -//│ end -//│ } -//│ fun class:A::getA() { -//│ set tmp = B(A, 2) in -//│ return tmp.getB() -//│ } -//│ } in -//│ set block$res1 = undefined in -//│ end :expect 3 A(1).getA() //│ = 3 +data class A1(x) with + data class B(y) with + fun getB() = x + y + fun getA() = (new B(2)).getB() + +:expect 3 +(new A1(1)).getA() +//│ = 3 + :sjs class A with val x = @@ -46,7 +26,7 @@ class A with g (new A).x() //│ JS (unsanitized): -//│ let g, A3, tmp1; +//│ let g, A3, tmp2; //│ g = function g() { //│ return 2 //│ }; @@ -61,8 +41,8 @@ class A with //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A"]; //│ }); -//│ tmp1 = globalThis.Object.freeze(new A3()); -//│ runtime.safeCall(tmp1.x()) +//│ tmp2 = globalThis.Object.freeze(new A3()); +//│ runtime.safeCall(tmp2.x()) //│ = 2 class A with diff --git a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls index a6b2e4d459..e628fd9870 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/EffectHandlers.mls @@ -8,23 +8,6 @@ module A with data class Test with f() val a = 1 -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.7: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :effectHandlers class Test with diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls index 8a2d594273..76d46be07a 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -6,21 +6,25 @@ import "../../mlscript-compile/Option.mls" :sjs module A with fun f(x) = x is Option.Some -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.7: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ JS (unsanitized): +//│ let A1; +//│ (class A { +//│ static { +//│ A1 = this +//│ } +//│ constructor() { +//│ runtime.Unit; +//│ } +//│ static #A_mod$cap; +//│ static f(x) { +//│ if (x instanceof Option.Some.class) { +//│ return true +//│ } else { +//│ return false +//│ } +//│ } +//│ #A$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); diff --git a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls index 1dbf43a838..2514664105 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls @@ -15,8 +15,7 @@ fun foo() = :expect 5 foo() fs.0() -//│ ═══[RUNTIME ERROR] Expected: '5', got: '2' -//│ = 2 +//│ = 5 let fs = mut [] @@ -45,26 +44,37 @@ fun foo() = set x += 1 return () => x //│ JS (unsanitized): -//│ let foo2, lambda2, lambda$2; -//│ lambda$2 = (undefined, function (x) { +//│ let foo2, lambda2, Capture$scope03, lambda$2; +//│ lambda$2 = (undefined, function (scope0$cap) { //│ return () => { -//│ return lambda2(x) +//│ return lambda2(scope0$cap) //│ } //│ }); -//│ lambda2 = (undefined, function (x) { -//│ return x +//│ lambda2 = (undefined, function (scope0$cap) { +//│ return scope0$cap.x$0 +//│ }); +//│ (class Capture$scope02 { +//│ static { +//│ Capture$scope03 = this +//│ } +//│ constructor(x$0) { +//│ this.x$0 = x$0; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); //│ foo2 = function foo() { -//│ let x, tmp2; -//│ x = 1; +//│ let x, tmp2, scope0$cap; +//│ scope0$cap = new Capture$scope03(x); +//│ scope0$cap.x$0 = 1; //│ lbl: while (true) { //│ let scrut, tmp3; //│ scrut = true; //│ if (scrut === true) { //│ let lambda$here; -//│ tmp3 = x + 1; -//│ x = tmp3; -//│ lambda$here = lambda$2(x); +//│ tmp3 = scope0$cap.x$0 + 1; +//│ scope0$cap.x$0 = tmp3; +//│ lambda$here = lambda$2(scope0$cap); //│ return lambda$here //│ } else { tmp2 = runtime.Unit; } //│ break; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 97312e6472..a402381709 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -11,25 +11,13 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 14 foo(10, 0) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M.foo (REPL10:1:447) -//│ at foo (REPL10:1:758) -//│ at REPL13:1:38 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' +//│ = 14 // * This works the same with classes. // * Note that we currently generate accessors, which is not ideal; @@ -42,10 +30,10 @@ fun foo(y) = (new M).foo() foo(10) //│ JS (unsanitized): -//│ let M3, foo1; -//│ (class M2 { +//│ let M2, foo1; +//│ (class M1 { //│ static { -//│ M3 = this +//│ M2 = this //│ } //│ constructor(y) { //│ this.y = y; @@ -60,14 +48,14 @@ foo(10) //│ }); //│ foo1 = function foo(y) { //│ let tmp; -//│ tmp = globalThis.Object.freeze(new M3(y)); +//│ tmp = globalThis.Object.freeze(new M2(y)); //│ return tmp.foo() //│ }; //│ foo1(10) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M2.foo (REPL16:1:283) +//│ at M1.foo (REPL16:1:283) //│ at foo (REPL16:1:602) //│ at REPL16:1:643 //│ at ContextifyScript.runInThisContext (node:vm:137:12) @@ -115,9 +103,12 @@ fun foo(y) = fun foo() = set y = 2 O +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. let o = foo(10) -//│ o = O { foo$cap: undefined, class: object O } +//│ o = O :re set o.x = 1 @@ -132,42 +123,80 @@ fun foo(x, y) = x + y fun foo3 = M.foo2() foo3 +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:M +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res1, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res2, $block$res -> block$res5, member:M -> M2, $selRes -> selRes, member:foo -> foo1, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, class:M -> M1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, member:Predef -> Predef, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:C -> C1, $block$res -> block$res, member:foo -> foo2, member:Capture$foo -> Capture$foo3, class:C -> C, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res4, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:Capture$foo -> Capture$foo) +//│ curThis = N +//│ bindings = HashMap(object:M -> M3, member:foo3 -> foo31, member:foo -> foo4) +//│ curThis = S of N +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' +//│ ║ l.124: fun foo3 = M.foo2() +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.120: object M with +//│ ║ ^^^^^^ +//│ ║ l.121: fun foo2() = +//│ ║ ^^^^^^^^^^^^^^^^ +//│ ║ l.122: set y = 2 +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.123: x + y +//│ ╙── ^^^^^^^^^^^ //│ JS (unsanitized): -//│ let M5, foo31, foo4; +//│ let foo31, foo4; //│ foo31 = function foo3() { -//│ return M5.foo2() +//│ return M.foo2() //│ }; -//│ (class M4 { -//│ static { -//│ new this -//│ } -//│ constructor(x, y) { -//│ M5 = this; -//│ this.x = x; -//│ this.y = y; -//│ Object.defineProperty(this, "class", { -//│ value: M4 -//│ }); -//│ globalThis.Object.freeze(this); -//│ } -//│ #M$cap; -//│ foo2() { -//│ this.y = 2; -//│ return this.x + this.y -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["object", "M"]; -//│ }); -//│ foo4 = function foo(x, y) { let tmp; tmp = foo31(); return tmp }; +//│ foo4 = function foo(x, y) { +//│ let M4, tmp; +//│ (class M3 { +//│ static { +//│ new this +//│ } +//│ constructor() { +//│ M4 = this; +//│ Object.defineProperty(this, "class", { +//│ value: M3 +//│ }); +//│ globalThis.Object.freeze(this); +//│ } +//│ #M$cap; +//│ foo2() { +//│ y = 2; +//│ return x + y +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["object", "M"]; +//│ }); +//│ tmp = foo31(); +//│ return tmp +//│ }; +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 12 foo(10, 0) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M4.foo2 (REPL44:1:555) -//│ at foo3 (REPL44:1:141) -//│ at foo (REPL44:1:847) +//│ ═══[RUNTIME ERROR] ReferenceError: M is not defined +//│ at foo3 (REPL44:1:134) +//│ at foo (REPL44:1:839) //│ at REPL47:1:39 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) @@ -175,6 +204,7 @@ foo(10, 0) //│ at REPLServer.runBound [as eval] (node:domain:444:12) //│ at REPLServer.onLine (node:repl:886:12) //│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -213,23 +243,44 @@ module M with fun get = x val hi = A() M.hi.get -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.219: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, member:foo3 -> foo31, member:foo -> foo4, object:M -> M3, $block$res -> block$res1, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, class:A -> A, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res13, class:Capture$foo -> Capture$foo) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($tmp -> tmp1) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ╟── which references the symbol introduced here +//│ ║ l.242: class A() with +//│ ║ ^^^^^^^^ +//│ ║ l.243: fun get = x +//│ ╙── ^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined +//│ at (REPL56:1:612) +//│ at REPL56:1:718 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' :expect 2 module M with @@ -238,23 +289,7 @@ module M with fun g() = x g M.f()() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.244: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ = 2 :expect 2 object M with @@ -280,26 +315,114 @@ module M with fun get = M.x + x val x = if A() is A then 2 else 3 M.A().get -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.287: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyCase (Lifter.scala:199) -//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:88) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:M -> M9, module:M -> M8, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $block$res -> block$res16, $selRes -> selRes4, class:M -> M8, $discarded -> discarded4, $tmp -> tmp1, member:foo3 -> foo31, member:foo -> foo4, member:g$ -> g$, object:M -> M3, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, member:g -> g1, class:A -> A, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $block$res -> block$res13, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = N +//│ bindings = HashMap(member:M -> M15, module:M -> M14, class:M -> M14, class:A -> A5, $tmp -> tmp3) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut, $tmp -> tmp4) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ╟── which references the symbol introduced here +//│ ║ l.277: class A() with +//│ ║ ^^^^^^^^ +//│ ║ l.278: fun get = M.x + x +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:M -> M9, module:M -> M8, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $block$res -> block$res16, $selRes -> selRes4, class:M -> M8, $discarded -> discarded4, $tmp -> tmp1, member:foo3 -> foo31, member:foo -> foo4, member:g$ -> g$, object:M -> M3, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, member:g -> g1, class:A -> A, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $block$res -> block$res13, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = N +//│ bindings = HashMap(member:M -> M15, module:M -> M14, class:M -> M14, class:A -> A5, $tmp -> tmp3) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut, $tmp -> tmp4) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ║ l.277: class A() with +//│ ║ ^^^^^^^^^^^^^^ +//│ ║ l.278: fun get = M.x + x +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.277: class A() with +//│ ║ ^^^^^^^^ +//│ ║ l.278: fun get = M.x + x +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ JS (unsanitized): +//│ let M15, tmp3; +//│ (class M14 { +//│ static { +//│ M15 = this +//│ } +//│ constructor() { +//│ runtime.Unit; +//│ } +//│ static #M_mod$cap; +//│ static { +//│ let scrut, tmp4; +//│ this.A = function A() { +//│ return globalThis.Object.freeze(new A.class()); +//│ }; +//│ (class A5 { +//│ static { +//│ M14.A.class = this +//│ } +//│ constructor() {} +//│ #A$cap; +//│ get get() { +//│ return M15.x + M14.x; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A", []]; +//│ }); +//│ scrut = A(); +//│ if (scrut instanceof A.class) { +//│ tmp4 = 2; +//│ } else { +//│ tmp4 = 3; +//│ } +//│ this.x = tmp4; +//│ } +//│ #M$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "M"]; +//│ }); +//│ tmp3 = M15.A(); +//│ tmp3.get +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined +//│ at (REPL68:1:604) +//│ at REPL68:1:796 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) module M with val x = 2 @@ -307,23 +430,7 @@ module M with fun get = x class B() extends A M.B().get -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.313: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ = 2 /// Top Level Modules /// @@ -340,47 +447,173 @@ module M with fun newB = B() 0 is A M.A(2).newB.newA_B(3).newB.get -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.343: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyCase (Lifter.scala:199) -//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut) +//│ curThis = S of S of class:A +//│ bindings = HashMap(x -> x) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ╟── which references the symbol introduced here +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut) +//│ curThis = S of S of class:A +//│ bindings = HashMap(x -> x) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut) +//│ curThis = S of S of class:A +//│ bindings = HashMap(x -> x) +//│ curThis = N +//│ bindings = HashMap() +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ╟── which references the symbol introduced here +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected compilation error +//│ FAILURE LOCATION: lookup_! (Scope.scala:114) +//│ FAILURE INFO: Tuple2: +//│ _1 = Tuple2: +//│ _1 = member:A +//│ _2 = class hkmc2.semantics.BlockMemberSymbol +//│ _2 = Scope: +//│ parent = S of Scope: +//│ parent = S of Scope: +//│ parent = N +//│ curThis = S of S of globalThis:globalThis +//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) +//│ curThis = S of S of module:M +//│ bindings = HashMap() +//│ curThis = N +//│ bindings = HashMap($scrut -> scrut) +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── which references the symbol introduced here +//│ ║ l.380: data class A(x) with +//│ ║ ^^^^^^^^^ +//│ ║ l.381: class B() with +//│ ║ ^^^^^^^^^^^^^^^^^^ +//│ ║ l.382: fun get = x +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.384: ... (more lines omitted) ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ FAILURE: Unexpected runtime error +//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) +//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined +//│ at (REPL74:1:1453) +//│ at REPL74:1:1576 +//│ at ContextifyScript.runInThisContext (node:vm:137:12) +//│ at REPLServer.defaultEval (node:repl:562:24) +//│ at bound (node:domain:433:15) +//│ at REPLServer.runBound [as eval] (node:domain:444:12) +//│ at REPLServer.onLine (node:repl:886:12) +//│ at REPLServer.emit (node:events:508:28) +//│ at REPLServer.emit (node:domain:489:12) +//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) module M with class A object B extends A -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.373: module M with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) :lift @@ -389,99 +622,35 @@ fun foo(x) = fun bar = x val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x$0') -//│ at new O2 (REPL62:1:194) -//│ at (REPL62:1:85) -//│ at REPL62:1:419 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. +//│ FAILURE: Unexpected warning +//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. foo(123) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: foo5 is not a function -//│ at REPL65:1:39 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ = [true, [true, false], true] module A with fun g = 1 fun f = () => g f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.430: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module A with val a = (() => 1)() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.452: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) // This is related to the above, but it will look into the ignored bms map module A with val x = fun f() = 1 f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.473: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) module A with val x = @@ -491,35 +660,15 @@ module A with h() f() + g() A.x -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: createMetadata (Lifter.scala:184) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.495: module A with -//│ ╙── ^ -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing -//│ at: scala.Predef$.$qmark$qmark$qmark(Predef.scala:344) -//│ at: hkmc2.Lifter.createRewritten(Lifter.scala:1047) -//│ at: hkmc2.Lifter.transform$$anonfun$1(Lifter.scala:1108) -//│ at: scala.collection.immutable.List.foreach(List.scala:323) -//│ at: hkmc2.Lifter.transform(Lifter.scala:1107) -//│ at: hkmc2.codegen.Lowering.program(Lowering.scala:1056) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:110) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:63) -//│ at: hkmc2.WasmDiffMaker.processTerm(WasmDiffMaker.scala:51) +//│ = 2 // private fields in modules -:fixme module A with let x = 1 fun mtd = fun nested() = x nested() A.mtd -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.523: module A with -//│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls index 816be05ef5..2487471824 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/PatternInFun.mls @@ -7,6 +7,6 @@ fun foo() = pattern P = Int x => x is P -//│ FAILURE: Unexpected lack of warnings +//│ ═══[WARNING] Cannot yet lift class `P` as it is used as a first-class class. diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index 4c87b65729..30e3a58e67 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -165,10 +165,9 @@ module A with fun g(x) = if x < 0 then 0 else @tailcall f(x) @tailcall g(x - 1) A.f(10000) -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.163: module A with -//│ ╙── ^ -//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing +//│ ═══[ERROR] Only direct calls in tail position may be marked @tailcall. +//│ ═══[ERROR] This tail call exits the current scope is not optimized. +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded // These calls are represented as field selections and don't yet have the explicitTailCall parameter. :breakme @@ -193,12 +192,12 @@ class A with fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.201: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Class methods may not yet be marked @tailrec. -//│ ║ l.192: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.201: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^ //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.193: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) +//│ ║ l.202: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //│ ╙── ^^^^^^^^ //│ = 0 From 5a7d7ad9aa33df2bf12831abca44a5bbac1853d6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 18 Jan 2026 19:13:20 +0800 Subject: [PATCH 12/18] fix more tests --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 52 +++--- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 40 +---- .../src/test/mlscript/backlog/Lifter.mls | 20 +-- .../mlscript/handlers/NonLocalReturns.mls | 18 +- .../mlscript/handlers/ReturnInHandler.mls | 4 +- .../test/mlscript/lifter/ModulesObjects.mls | 159 +++--------------- .../src/test/mlscript/tailrec/TailRecOpt.mls | 6 +- 7 files changed, 74 insertions(+), 225 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 661dd27def..fe38e63204 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -17,12 +17,6 @@ import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.collection.mutable.ListBuffer -import hkmc2.ScopeData.ScopedObject.Top -import hkmc2.ScopeData.ScopedObject.Companion -import hkmc2.ScopeData.ScopedObject.ClassCtor -import hkmc2.ScopeData.ScopedObject.Func -import hkmc2.ScopeData.ScopedObject.Loop -import hkmc2.ScopeData.ScopedObject.ScopedBlock object Lifter: @@ -148,7 +142,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): enum DefnRef: case Sym(l: Local) case InScope(l: BlockMemberSymbol, d: DefinitionSymbol[?]) - case Field(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, l: BlockMemberSymbol, d: DefinitionSymbol[?]) + case Field(isym: InnerSymbol, l: BlockMemberSymbol, d: DefinitionSymbol[?]) def read(using ctx: LifterCtxNew): Path = this match case Sym(l) => l.asPath @@ -266,7 +260,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyValue(v: Value): Unit = v match case RefOfBms(_, S(l)) if nestedScopes.contains(l) => data.getNode(l).obj match - case c: (ScopedObject.Class | ClassCtor) => + case c: (ScopedObject.Class | ScopedObject.ClassCtor) => if !c.node.get.isInTopLevelMod then raise(WarningReport( msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, @@ -274,9 +268,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise): )) val isym = c match case c: ScopedObject.Class => c.cls.isym - case c: ClassCtor => c.cls.isym + case c: ScopedObject.ClassCtor => c.cls.isym ignored += isym - case Func(fun, isMethod) => firstClsFns += fun.dSym + case ScopedObject.Func(fun, isMethod) => firstClsFns += fun.dSym case _ => super.applyValue(v) case _ => super.applyValue(v) @@ -394,7 +388,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // Other naked references to BlockMemberSymbols. case _ => ctx.defnsMap.get(d) match - case Some(value) => k(value.read) + case Some(value) => + println(p) + println(value) + k(value.read) case None => super.applyPath(p)(k) case _ => super.applyPath(p)(k) @@ -712,10 +709,22 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // BMS refs from ignored defns // Note that we map the DefinitionSymbol to the disambiguated BMS. - protected lazy val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = + protected val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = node.children.collect: case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => - r.sym -> DefnRef.InScope(r.bsym, r.sym) + // Objects in a ctor may or may not be nested in a scoped block, + // we cannot simply inspect the parent node of the definition to + // see if it belongs to some class like object. + val owner = r match + case ScopedObject.Class(cls) => cls.owner + case ScopedObject.Companion(comp, par) => par.owner + case ScopedObject.ClassCtor(cls) => N + case ScopedObject.Func(fun, isMethod) => fun.owner + val path = owner match + case Some(value) => DefnRef.Field(value, r.bsym, r.sym) + case None => DefnRef.InScope(r.bsym, r.sym) + + r.sym -> path .toMap lazy val defnPaths: Map[DefinitionSymbol[?], DefnRef] = defnPathsFromThisObj @@ -816,14 +825,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): protected def addCaptureSym(b: Block): Block = addCaptureSym(b, captureSym, true) - sealed trait ClsLikeRewrittenScope[T](isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol) extends RewrittenScope[T]: - // always select using `this` - override lazy val defnPathsFromThisObj = - node.children.collect: - case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => - r.sym -> DefnRef.Field(isym, r.bsym, r.sym) - .toMap - // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) @@ -878,8 +879,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): def getRewrittenCls = ctx.rewrittenScopes(obj.cls.isym) class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) - extends RewrittenScope[ClsLikeDefn](obj) - with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): + extends RewrittenScope[ClsLikeDefn](obj): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -901,8 +901,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) class RewrittenCompanion(override val obj: ScopedObject.Companion)(using ctx: LifterCtxNew) - extends RewrittenScope[ClsLikeBody](obj) - with ClsLikeRewrittenScope[ClsLikeBody](obj.comp.isym): + extends RewrittenScope[ClsLikeBody](obj): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.comp.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -1027,8 +1026,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): if isTrivial then LifterResult(lifted, extra) else LifterResult(lifted, mkAuxDefn :: extra) class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) - extends LiftedScope[ClsLikeDefn](obj) - with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): + extends LiftedScope[ClsLikeDefn](obj): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 6809d109b0..135c6c8a00 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,42 +9,4 @@ :todo :noSanityCheck -:lift -:ssjs -fun f(x) = - class A with - fun get = x - module A - fun g = new A -//│ Elab: { fun member:f‹873›(x‹874›) = { Cls A { method fun member:get‹870› = x‹874›#666; }; Mod A { }; fun member:g‹872› = new member:A‹871›#666; () }; } -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.17: module A -//│ ╙── ^ -//│ JS: -//│ let g, f, g$; -//│ g$ = function g$(A$) { -//│ return () => { -//│ return g(A$) -//│ } -//│ }; -//│ g = function g(A$) { -//│ return globalThis.Object.freeze(new A$()) -//│ }; -//│ f = function f(x) { -//│ let A1; -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() {} -//│ static #A_mod$cap; -//│ #A$cap; -//│ get get() { -//│ return x; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); -//│ return runtime.Unit -//│ }; -//│ block$res1 = undefined; + diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 0bd3ec0730..1c16a7d7d0 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -155,7 +155,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.157: module M with +//│ ║ l.151: module M with //│ ╙── ^ :expect 14 @@ -170,19 +170,19 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.172: module M with +//│ ║ l.166: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' -//│ ║ l.176: fun foo = M.foo() +//│ ║ l.170: fun foo = M.foo() //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.172: module M with +//│ ║ l.166: module M with //│ ║ ^^^^^^ -//│ ║ l.173: fun foo() = +//│ ║ l.167: fun foo() = //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.174: set y = 2 +//│ ║ l.168: set y = 2 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.175: x + y +//│ ║ l.169: x + y //│ ╙── ^^^^^^^^^^^ :expect 12 @@ -196,7 +196,7 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.201: module M with +//│ ║ l.195: module M with //│ ╙── ^ :expect 2 @@ -212,7 +212,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.217: class Bar extends Foo +//│ ║ l.211: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. @@ -229,7 +229,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.232: module M with +//│ ║ l.226: module M with //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index 4be34e1871..05fe693cbe 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -7,7 +7,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -27,7 +27,7 @@ fun foo(x) = let xs = [() => return x + 1] xs.0() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -92,10 +92,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -124,10 +124,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -147,7 +147,7 @@ f() fun f() = () => return 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -166,7 +166,7 @@ f()() fun f(): () -> Int = () => return () => 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -217,7 +217,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 2131c4ac86..968b07d41b 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -17,7 +17,7 @@ fun f() = return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -43,7 +43,7 @@ fun f() = return 3 10 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:254) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index a402381709..e644b39514 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -12,7 +12,7 @@ fun foo(x, y) = x + y + test M.foo() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 14 @@ -104,7 +104,7 @@ fun foo(y) = set y = 2 O //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. let o = foo(10) @@ -124,7 +124,7 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) @@ -187,7 +187,7 @@ fun foo(x, y) = //│ return tmp //│ }; //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 12 @@ -336,38 +336,9 @@ M.A().get //│ bindings = HashMap($scrut -> scrut, $tmp -> tmp4) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' //│ ╟── which references the symbol introduced here -//│ ║ l.277: class A() with +//│ ║ l.314: class A() with //│ ║ ^^^^^^^^ -//│ ║ l.278: fun get = M.x + x -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:M -> M9, module:M -> M8, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $block$res -> block$res16, $selRes -> selRes4, class:M -> M8, $discarded -> discarded4, $tmp -> tmp1, member:foo3 -> foo31, member:foo -> foo4, member:g$ -> g$, object:M -> M3, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, member:g -> g1, class:A -> A, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $block$res -> block$res13, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = N -//│ bindings = HashMap(member:M -> M15, module:M -> M14, class:M -> M14, class:A -> A5, $tmp -> tmp3) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut, $tmp -> tmp4) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.277: class A() with -//│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.278: fun get = M.x + x -//│ ║ ^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.277: class A() with -//│ ║ ^^^^^^^^ -//│ ║ l.278: fun get = M.x + x +//│ ║ l.315: fun get = M.x + x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ JS (unsanitized): //│ let M15, tmp3; @@ -397,7 +368,7 @@ M.A().get //│ static [definitionMetadata] = ["class", "A", []]; //│ }); //│ scrut = A(); -//│ if (scrut instanceof A.class) { +//│ if (scrut instanceof M14.A.class) { //│ tmp4 = 2; //│ } else { //│ tmp4 = 3; @@ -414,7 +385,7 @@ M.A().get //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] ReferenceError: A is not defined //│ at (REPL68:1:604) -//│ at REPL68:1:796 +//│ at REPL68:1:800 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -471,15 +442,15 @@ M.A(2).newB.newA_B(3).newB.get //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' //│ ╟── which references the symbol introduced here -//│ ║ l.380: data class A(x) with +//│ ║ l.442: data class A(x) with //│ ║ ^^^^^^^^^ -//│ ║ l.381: class B() with +//│ ║ l.443: class B() with //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x +//│ ║ l.444: fun get = x //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ l.445: fun newA_B(y) = A(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... +//│ ║ l.446: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) @@ -504,104 +475,22 @@ M.A(2).newB.newA_B(3).newB.get //│ curThis = N //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.380: data class A(x) with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.381: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.380: data class A(x) with -//│ ║ ^^^^^^^^^ -//│ ║ l.381: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut) -//│ curThis = S of S of class:A -//│ bindings = HashMap(x -> x) -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ╟── which references the symbol introduced here -//│ ║ l.380: data class A(x) with -//│ ║ ^^^^^^^^^ -//│ ║ l.381: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ║ l.380: data class A(x) with -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.381: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── which references the symbol introduced here -//│ ║ l.380: data class A(x) with +//│ ║ l.442: data class A(x) with //│ ║ ^^^^^^^^^ -//│ ║ l.381: class B() with +//│ ║ l.443: class B() with //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.382: fun get = x +//│ ║ l.444: fun get = x //│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.383: fun newA_B(y) = A(y) +//│ ║ l.445: fun newA_B(y) = A(y) //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.384: ... (more lines omitted) ... +//│ ║ l.446: ... (more lines omitted) ... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ at (REPL74:1:1453) -//│ at REPL74:1:1576 +//│ at get newB (REPL74:1:1250) +//│ at REPL74:1:1688 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -623,16 +512,16 @@ fun foo(x) = val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:278) +//│ FAILURE LOCATION: applyValue (Lifter.scala:265) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. foo(123) diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index 30e3a58e67..b6e34db602 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -192,12 +192,12 @@ class A with fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.201: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.191: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Class methods may not yet be marked @tailrec. -//│ ║ l.201: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.191: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^ //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.202: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) +//│ ║ l.192: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //│ ╙── ^^^^^^^^ //│ = 0 From 6a6ac4ace99b4403e416bf740ad2d9758fe8a153 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 18 Jan 2026 21:24:10 +0800 Subject: [PATCH 13/18] fix problem with modules --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 88 +++-- .../main/scala/hkmc2/codegen/ScopeData.scala | 28 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 16 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 334 ++---------------- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 45 ++- .../src/test/mlscript/backlog/Lifter.mls | 68 +--- .../test/mlscript/handlers/BadHandlers.mls | 2 +- .../src/test/mlscript/handlers/Effects.mls | 8 +- .../mlscript/handlers/HandlersInClasses.mls | 6 +- .../mlscript/handlers/NonLocalReturns.mls | 18 +- .../mlscript/handlers/RecursiveHandlers.mls | 2 +- .../mlscript/handlers/ReturnInHandler.mls | 6 +- .../src/test/mlscript/lifter/DefnsInClass.mls | 9 + .../src/test/mlscript/lifter/FunInFun.mls | 13 + .../test/mlscript/lifter/ModulesObjects.mls | 186 ++-------- 15 files changed, 227 insertions(+), 602 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index fe38e63204..42b783020b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -123,13 +123,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case Sym(l: Local) case BmsRef(l: BlockMemberSymbol, d: DefinitionSymbol[?]) case InCapture(capturePath: Path, field: TermSymbol) - case PubField(isym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol, tsym: TermSymbol) def read(using ctx: LifterCtxNew): Path = this match case Sym(l) => l.asPath case BmsRef(l, d) => Value.Ref(l, S(d)) case InCapture(path, field) => Select(path, field.id)(S(field)) - case PubField(isym, sym, tsym) => Select(ctx.symbolsMap(isym).read, Tree.Ident(sym.nme))(S(tsym)) def asArg(using ctx: LifterCtxNew) = read.asArg @@ -137,7 +135,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): case Sym(l) => Assign(l, value, rest) case BmsRef(l, d) => lastWords("Tried to assign to a BlockMemberSymbol") case InCapture(path, field) => AssignField(path, field.id, value, rest)(S(field)) - case PubField(isym, sym, tsym) => AssignField(ctx.symbolsMap(isym).read, Tree.Ident(sym.nme), value, rest)(S(tsym)) enum DefnRef: case Sym(l: Local) @@ -325,29 +322,44 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val walker = new BlockDataTransformer(SymbolSubst()): // only scan within the block. don't traverse + def resolveDefnRef(l: BlockMemberSymbol, d: DefinitionSymbol[?], r: RewrittenScope[?]) = + ctx.defnsMap.get(d) match + case Some(defnRef) => S(defnRef.read) + case None => + ctx.defnsMap.get(d) match + case Some(value) => S(value.read) + case None => r.obj match + case r: ScopedObject.Referencable[?] => + // rewrite the parent + r.owner.flatMap(ctx.symbolsMap.get(_)) match + case Some(value) => + S(Select(value.read, Tree.Ident(l.nme))(S(d))) + case None => N + case _ => N + override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version case c @ Call(RefOfBms(l, S(d)), args) => - def join = ctx.defnsMap.get(d) match - case Some(value) => c.copy(fun = value.read)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) - case None => c - val newCall = ctx.rewrittenScopes.get(d) match + val newCall: Call = ctx.rewrittenScopes.get(d) match case None => c - case Some(value) => value match + case Some(r) => + def join = + resolveDefnRef(l, d, r) match + case Some(value) => c.copy(fun = value)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) + case None => c + r match // function call case f: LiftedFunc => f.rewriteCall(c) // ctor call (without using `new`) case ctor: RewrittenClassCtor => ctor.getRewrittenCls match case cls: LiftedClass => cls.rewriteCall(c) - case _ => ctx.defnsMap.get(d) match - case Some(value) => join - case None => c + case _ => join case _ => join applyArgs(newCall.args): newArgs => if (newCall.args is newArgs) && (c is newCall) then k(newCall) else k(Call(newCall.fun, newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) - case inst @ Instantiate(mut, RefOfBms(l, S(d)), args) => + case inst @ Instantiate(mut, RefOfBms(l, S(d)), args) => // It is VERY IMPORTANT that we rewrite it like this and not using super.applyResult. // The reason is that Instantiate is disambiguated using the class's InnerSymbol, which is also // used to represent the class's `this`. super.applyResult would apply super.applyPath on the @@ -355,9 +367,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise): // adds `this -> this` to the symbols map. val newInst = ctx.rewrittenScopes.get(d) match case S(c: LiftedClass) => c.rewriteInstantiate(inst) - case _ => ctx.defnsMap.get(d) match - case Some(value) => Instantiate(inst.mut, value.read, inst.args) + case S(r) => resolveDefnRef(l, d, r) match + case Some(value) => Instantiate(inst.mut, value, inst.args) case None => inst + case N => inst applyArgs(newInst.args): newArgs => if (newInst.args is newArgs) && (newInst is inst) then k(newInst) @@ -387,12 +400,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise): k(Value.Ref(newSym, N)) // Other naked references to BlockMemberSymbols. - case _ => ctx.defnsMap.get(d) match - case Some(value) => - println(p) - println(value) - k(value.read) + case S(r) => resolveDefnRef(l, d, r) match + case Some(value) => k(value) case None => super.applyPath(p)(k) + case _ => super.applyPath(p)(k) case _ => super.applyPath(p)(k) (walker.applyBlock(b), syms.toList, extraLocals) @@ -685,8 +696,6 @@ class Lifter(topLevelBlk: Block)(using State, Raise): */ protected final def pathsFromThisObj: Map[Local, LocalPath] = // Remove child BlockMemberSymbols; we will use their definition symbols instead - val childrenBms = node.children.collect: - case ScopeNode(obj = r: ScopedObject.Referencable[?]) => r.bsym // Locals introduced by this object val fromThisObj = node.localsWithoutBms @@ -699,31 +708,29 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val tSym = captureMap(s) s -> LocalPath.InCapture(capturePath, tSym) .toMap + // Inner symbols of nested modules and objects + val isyms = node.children + .collect: + case ScopeNode(obj = c: ScopedObject.Companion) => + val s: Local = c.comp.isym + s -> LocalPath.BmsRef(c.bsym, c.comp.isym) + .toMap // Note: the order here is important, as fromCap must override keys from // fromThisObj. - fromThisObj ++ fromCap + isyms ++ fromThisObj ++ fromCap lazy val capturePaths = if thisCapturedLocals.isEmpty then Map.empty else Map(obj.toInfo -> capturePath) - // BMS refs from ignored defns + // BMS refs from ignored defns (including child defns of modules) // Note that we map the DefinitionSymbol to the disambiguated BMS. protected val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = node.children.collect: case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => - // Objects in a ctor may or may not be nested in a scoped block, - // we cannot simply inspect the parent node of the definition to - // see if it belongs to some class like object. - val owner = r match - case ScopedObject.Class(cls) => cls.owner - case ScopedObject.Companion(comp, par) => par.owner - case ScopedObject.ClassCtor(cls) => N - case ScopedObject.Func(fun, isMethod) => fun.owner - val path = owner match - case Some(value) => DefnRef.Field(value, r.bsym, r.sym) + val path = r.owner match + case Some(isym) => DefnRef.Field(isym, r.bsym, r.sym) case None => DefnRef.InScope(r.bsym, r.sym) - r.sym -> path .toMap @@ -734,6 +741,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise): /** Represents a scoped object that is to be rewritten and lifted. */ sealed abstract class LiftedScope[T <: Defn](override val obj: ScopedObject.Liftable[T])(using ctx: LifterCtxNew) extends RewrittenScope[T](obj): private val AccessInfo(accessed, _, refdScopes) = usedVars.accessMap(obj.toInfo) + private val AccessInfo(_, _, allRefdScopes) = usedVars.accessMapWithIgnored(obj.toInfo) private val refdDSyms = refdScopes.collect: case d: LiftedSym => d .toSet @@ -741,8 +749,18 @@ class Lifter(topLevelBlk: Block)(using State, Raise): /** Symbols that this object will lose access to once lifted, and therefore must receive * as a parameter. Does not include neighbouring objects that this definition may lose * access to. Those are in a separate list. + * + * Includes inner symbols introduced by modules. */ - final val reqSymbols = accessed + final val reqSymbols = accessed ++ allRefdScopes.map(data.getNode(_).obj) + .collect: + case s: ScopedObject.Referencable[?] if s.owner.isDefined => s.owner.get + .collect: + case d: DefinitionSymbol[?] => d + .filter: d => + data.getNode(d).obj match + case _: ScopedObject.Companion => true + case _ => false private val (reqPassedSymbols, captures) = reqSymbols .partitionMap: s => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index e82ad00f5d..f312179d45 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -13,12 +13,6 @@ import hkmc2.codegen.llir.FreshInt import java.util.IdentityHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet -import hkmc2.ScopeData.ScopedObject.Top -import hkmc2.ScopeData.ScopedObject.Companion -import hkmc2.ScopeData.ScopedObject.ClassCtor -import hkmc2.ScopeData.ScopedObject.Func -import hkmc2.ScopeData.ScopedObject.Loop -import hkmc2.ScopeData.ScopedObject.ScopedBlock object ScopeData: opaque type ScopeUID = BigInt @@ -111,6 +105,12 @@ object ScopeData: case Companion(comp, par) => par.sym case Func(fun, isMethod) => fun.sym case ClassCtor(cls) => cls.sym + def owner: Opt[InnerSymbol] = this match + case Class(cls) => cls.owner + case Companion(comp, par) => par.owner + case ClassCtor(cls) => cls.owner + case Func(fun, isMethod) => fun.owner + // Scoped nodes which could possibly be lifted to the top level. sealed abstract class Liftable[T <: Defn] extends Referencable[T]: @@ -196,9 +196,9 @@ object ScopeData: lazy val isInTopLevelMod: Bool = parent match case Some(par) => par.obj match - case _: Companion => par.isInTopLevelMod - case s: ScopedBlock => s.node.get.parent.get.obj match - case c: Companion => c.node.get.parent.get.isInTopLevelMod + case _: ScopedObject.Companion => par.isInTopLevelMod + case s: ScopedObject.ScopedBlock => s.node.get.parent.get.obj match + case c: ScopedObject.Companion => c.node.get.parent.get.isInTopLevelMod case _ => false case _ => false case None => true @@ -210,6 +210,16 @@ object ScopeData: case c @ ScopeNode(obj = s: ScopedObject.Referencable[?]) => s.bsym obj.definedLocals -- rmv case _ => obj.definedLocals + + lazy val inScopeISyms: Set[Local] = + val parVals = parent match + case Some(value) => value.inScopeISyms + case None => Set.empty + + obj match + case c: ScopedObject.Class => parVals + c.cls.isym + case c: ScopedObject.Companion => parVals + c.comp.isym + case _ => parVals // All of the following must not be called until ignoredScopes is populated with the relevant data. diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 9bd6dd71a3..1dea65b89e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -18,12 +18,6 @@ import scala.jdk.CollectionConverters.* import java.util.IdentityHashMap import java.util.Collections import scala.collection.mutable.Buffer -import hkmc2.ScopeData.ScopedObject.Top -import hkmc2.ScopeData.ScopedObject.Companion -import hkmc2.ScopeData.ScopedObject.ClassCtor -import hkmc2.ScopeData.ScopedObject.Func -import hkmc2.ScopeData.ScopedObject.Loop -import hkmc2.ScopeData.ScopedObject.ScopedBlock object UsedVarAnalyzer: case class MutAccessInfo( @@ -82,10 +76,14 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // If so, then it requires reading the class symbol val node = scopeData.getNode(dSym) node.obj match - case f @ Func(isMethod = S(true)) => accessed.accessed.add(f.fun.owner.get) - case Func(isMethod = N | S(false)) => accessed.refdDefns.add(node.obj.toInfo) - case _: ScopedObject.Class | _: ClassCtor | _: Companion => accessed.refdDefns.add(node.obj.toInfo) + // for definitions nested inside a class: they need the InnerSymbol of the class instance + case f @ ScopedObject.Func(isMethod = S(true)) => accessed.accessed.add(f.fun.owner.get) + // definitions that access a module's method directly need an edge to that method + case ScopedObject.Func(isMethod = N | S(false)) => + accessed.refdDefns.add(node.obj.toInfo) + case _: ScopedObject.Class | _: ScopedObject.ClassCtor => accessed.refdDefns.add(node.obj.toInfo) case _ => () + case Value.Ref(l, _) => accessed.accessed.add(l) case _ => super.applyPath(p) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index ab71773209..5365663ac0 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,317 +7,33 @@ :global :d :todo -:noSanityCheck -class Eff -//│ Elab: { Cls Eff { }; } -:effectHandlers -:lift -:noSanityCheck -:slot -handle h1 = Eff with - fun perform()(k) = - print("h1") - k() -handle h2 = Eff with - fun perform()(k) = - print("h2") - h1.perform() - k() -handle h3 = Eff with - fun perform()(k) = - print("h3") - h2.perform() - k() -h1.perform() -h3.perform() -//│ Elab: { handle h1‹694› = Ref(member:Eff‹686›)() List(HandlerTermDefinition(k‹699›,TermDefinition(Fun,member:perform‹698›,term:class:Handler$h1$‹695›.perform‹704›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h1)),None))))),App(Ref(k‹699›),Tup(List())))),‹result of member:perform‹698››‹703›,‹method ›,Modulefulness(None),List(),None))) in Handle(h2‹705›,Ref(member:Eff‹686›),List(),class:Handler$h2$‹706›,List(HandlerTermDefinition(k‹710›,TermDefinition(Fun,member:perform‹709›,term:class:Handler$h2$‹706›.perform‹716›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h2)),None)))), App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Ref(k‹710›),Tup(List())))),‹result of member:perform‹709››‹715›,‹method ›,Modulefulness(None),List(),None))),Handle(h3‹717›,Ref(member:Eff‹686›),List(),class:Handler$h3$‹718›,List(HandlerTermDefinition(k‹722›,TermDefinition(Fun,member:perform‹721›,term:class:Handler$h3$‹718›.perform‹728›,List(ParamList(‹›,List(),None)),None,None,Some(Blk(List(App(SynthSel(Ref(member:Predef),Ident(print)),Tup(List(Fld(‹›,Lit(StrLit(h3)),None)))), App(Sel(Ref(h2‹705›),Ident(perform)),Tup(List()))),App(Ref(k‹722›),Tup(List())))),‹result of member:perform‹721››‹727›,‹method ›,Modulefulness(None),List(),None))),Blk(List(App(Sel(Ref(h1‹694›),Ident(perform)),Tup(List()))),App(Sel(Ref(h3‹717›),Ident(perform)),Tup(List()))))) } -//│ Pretty Lowered: -//│ -//│ define fun Handler$h2$perform(k) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc = 0 in -//│ end -//│ else -//│ set pc = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main = match pc -//│ 0 => -//│ set runtime.resumeValue = Predef.print("h2") in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$perform, 2, "HkScratch.mls:25:5", null, null, 1, 1, k, 0) -//│ in -//│ set pc = 2 in -//│ continue main -//│ 1 => -//│ set tmp = runtime.resumeValue in -//│ return k() -//│ 2 => -//│ set tmp1 = runtime.resumeValue in -//│ set runtime.resumeValue = ‹not in scope: h1‹694››.perform() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$perform, 1, "HkScratch.mls:26:5", null, null, 1, 1, k, 0) -//│ in -//│ set pc = 1 in -//│ continue main -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun Handler$h3$perform$(h2)(k1) { -//│ return Handler$h3$perform(h2, k1) -//│ } in -//│ define fun Handler$h3$perform(h21, k2) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc1 = 0 in -//│ end -//│ else -//│ set pc1 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main1 = match pc1 -//│ 0 => -//│ set runtime.resumeValue = Predef.print("h3") in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h3$perform, 2, "HkScratch.mls:30:5", null, null, 1, 2, h21, k2, 0) -//│ in -//│ set pc1 = 2 in -//│ continue main1 -//│ 1 => -//│ set tmp2 = runtime.resumeValue in -//│ return k2() -//│ 2 => -//│ set tmp3 = runtime.resumeValue in -//│ set runtime.resumeValue = h21.perform() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h3$perform, 1, "HkScratch.mls:31:7", null, null, 1, 2, h21, k2, 0) -//│ in -//│ set pc1 = 1 in -//│ continue main1 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun handleBlock$$(h3)() { -//│ return handleBlock$(h3) -//│ } in -//│ define class Handler$h3$ { -//│ let Handler$h3$$cap = ... -//│ preCtor { -//│ set tmp4 = super() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp4 = runtime.illegalEffect("in a constructor") in -//│ end -//│ in -//│ return tmp4 +:sjs +class A with + let y = 2 + fun f() = + set y = 3 +let a = new A +a.f() +//│ Elab: { Cls A { let term:class:A‹688›.y‹690›; term:class:A‹688›.y‹690› = 2; method fun member:f‹686›() = term:class:A‹688›.y‹690›#666 := 3; }; let a‹694›; a‹694› = new member:A‹687›#666; a‹694›#666.f() } +//│ JS (unsanitized): +//│ let A1, a; +//│ (class A { +//│ static { +//│ A1 = this //│ } -//│ ctor { -//│ set h23 = h22 in -//│ end +//│ constructor() { +//│ this.#y = 2; //│ } -//│ fun class:Handler$h3$‹718›::perform() { -//│ set Handler$h3$perform$here = Handler$h3$perform$(h2) in -//│ return runtime.mkEffect(Handler$h3$, Handler$h3$perform$here) +//│ #y; +//│ f() { +//│ this.#y = 3; +//│ return runtime.Unit //│ } -//│ } in -//│ define fun handleBlock$(h31) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc2 = 0 in -//│ end -//│ else -//│ set pc2 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main2 = match pc2 -//│ 0 => -//│ set runtime.resumeValue = ‹not in scope: h1‹694››.perform() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(handleBlock$, 1, "HkScratch.mls:33:1", null, null, 1, 1, h31, 0) -//│ in -//│ set pc2 = 1 in -//│ continue main2 -//│ 1 => -//│ set tmp5 = runtime.resumeValue in -//│ return h31.perform() -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun handleBlock$$(h24)() { -//│ return handleBlock$(h24) -//│ } in -//│ define class Handler$h2$ { -//│ let Handler$h2$$cap = ... -//│ preCtor { -//│ set tmp6 = super() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp6 = runtime.illegalEffect("in a constructor") in -//│ end -//│ in -//│ return tmp6 -//│ } -//│ fun class:Handler$h2$‹706›::perform() { -//│ return runtime.mkEffect(Handler$h2$, Handler$h2$perform) -//│ } -//│ } in -//│ define fun handleBlock$(h25) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc3 = 0 in -//│ end -//│ else -//│ set pc3 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main3 = match pc3 -//│ 0 => -//│ set h32 = new mut Handler$h3$(h25) in -//│ set handleBlock$$here = handleBlock$$(h32) in -//│ set runtime.resumeValue = runtime.enterHandleBlock(h32, handleBlock$$here) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 1, h25, 0) -//│ in -//│ set pc3 = 1 in -//│ continue main3 -//│ 1 => -//│ set tmp7 = runtime.resumeValue in -//│ return tmp7 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun Handler$h1$perform(k3) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc4 = 0 in -//│ end -//│ else -//│ set pc4 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main4 = match pc4 -//│ 0 => -//│ set runtime.resumeValue = Predef.print("h1") in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$perform, 1, "HkScratch.mls:21:5", null, null, 1, 1, k3, 0) -//│ in -//│ set pc4 = 1 in -//│ continue main4 -//│ 1 => -//│ set tmp8 = runtime.resumeValue in -//│ return k3() -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define class Handler$h1$ { -//│ let Handler$h1$$cap = ... -//│ preCtor { -//│ set tmp9 = super() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp9 = runtime.illegalEffect("in a constructor") in -//│ end -//│ in -//│ return tmp9 -//│ } -//│ fun class:Handler$h1$‹695›::perform() { -//│ return runtime.mkEffect(Handler$h1$, Handler$h1$perform) -//│ } -//│ } in -//│ set h1 = new mut Handler$h1$() in -//│ define fun handleBlock$() { -//│ match runtime.resumePc -//│ -1 => -//│ set pc5 = 0 in -//│ end -//│ else -//│ set pc5 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main5 = match pc5 -//│ 0 => -//│ set h26 = new mut Handler$h2$() in -//│ set handleBlock$$here1 = handleBlock$$(h26) in -//│ set runtime.resumeValue = runtime.enterHandleBlock(h26, handleBlock$$here1) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 0, 0) -//│ in -//│ set pc5 = 1 in -//│ continue main5 -//│ 1 => -//│ set tmp10 = runtime.resumeValue in -//│ return tmp10 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ set tmp11 = runtime.enterHandleBlock(h1, handleBlock$) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp11 = runtime.topLevelEffect(false) in -//│ end -//│ in -//│ set block$res2 = tmp11 in -//│ return undefined -//│ > h1 -//│ > h3 -//│ > h2 -//│ > h1 +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); +//│ a = globalThis.Object.freeze(new A1()); +//│ runtime.safeCall(a.f()) +//│ a = A diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 135c6c8a00..2cc21dc38b 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,4 +9,47 @@ :todo :noSanityCheck - +:sjs +:lift +fun f = + module M with + fun foo() = 2 + fun bar = M.foo + bar +//│ Elab: { fun member:f‹873› = { Mod M { fun member:foo‹870›() = 2; }; fun member:bar‹871› = member:M‹872›#666.foo‹member:foo‹870››; member:bar‹871›#666 }; } +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.15: module M with +//│ ╙── ^ +//│ JS (unsanitized): +//│ let bar, f, bar$; +//│ bar$ = function bar$(M1) { +//│ return () => { +//│ return bar(M1) +//│ } +//│ }; +//│ bar = function bar(M1) { +//│ return M1.foo +//│ }; +//│ f = function f() { +//│ let M1, tmp; +//│ (class M { +//│ static { +//│ M1 = this +//│ } +//│ constructor() { +//│ runtime.Unit; +//│ } +//│ static #M_mod$cap; +//│ static foo() { +//│ return 2 +//│ } +//│ #M$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "M"]; +//│ }); +//│ tmp = bar(M1); +//│ return tmp +//│ }; +//│ ╔══[WARNING] Modules are not yet lifted. +//│ ║ l.15: module M with +//│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 1c16a7d7d0..7b451d8a61 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -2,22 +2,6 @@ :lift :todo - -// The following are problems with lifting functions inside other functions. - - -// Lifting functions with spread arguments is broken. -:expect [1] -fun f(x) = - fun g(...rest) = - print(x) - rest - let a = g - a(1) -f(2) -//│ > 2 -//│ = [1] - // The following are problems with lifting classes inside other definitions. // We use the optional `symbol` parameter of `Select` to detect references to the @@ -126,24 +110,6 @@ test(2) //│ ═══[WARNING] Cannot yet lift definition `B` as it extends an unliftable class. //│ = 2 -:expect 2 -class A(a) with - let x = 2 - fun f() = - fun g() = x - g() -A(2).f() -//│ = 2 - -:expect 2 -module M with - let x = 2 - fun f() = - fun g() = x - g -M.f()() -//│ = 2 - /// The following are related to modules and objects. /// :todo @@ -155,7 +121,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.151: module M with +//│ ║ l.117: module M with //│ ╙── ^ :expect 14 @@ -170,25 +136,12 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.166: module M with +//│ ║ l.132: module M with //│ ╙── ^ -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' -//│ ║ l.170: fun foo = M.foo() -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.166: module M with -//│ ║ ^^^^^^ -//│ ║ l.167: fun foo() = -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.168: set y = 2 -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.169: x + y -//│ ╙── ^^^^^^^^^^^ :expect 12 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: M is not defined -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' +//│ = 12 data class A(x) with @@ -196,12 +149,19 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.195: module M with +//│ ║ l.159: module M with //│ ╙── ^ +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' +//│ ╟── which references the symbol introduced here +//│ ║ l.159: module M with +//│ ║ ^^^^^^ +//│ ║ l.160: fun getB() = x +//│ ╙── ^^^^^^^^^^^^^^^^^^ :expect 2 A(2).getA() -//│ = 2 +//│ ═══[RUNTIME ERROR] ReferenceError: M is not defined +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' // TODO: Foo needs to be put in a mutable capture. Also, we need to pass the Foo instance itself into Foo fun foo(x) = @@ -212,7 +172,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.211: class Bar extends Foo +//│ ║ l.182: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. @@ -229,7 +189,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.226: module M with +//│ ║ l.197: module M with //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls index 0c5bc7786c..a1e1200c77 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls @@ -62,7 +62,7 @@ object Foo with :re Foo.x //│ ╔══[ERROR] Object 'Foo' does not contain member 'x' -//│ ║ l.63: Foo.x +//│ ║ l.86: Foo.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 4eb93ce2b4..7e9f9bce9e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -443,16 +443,16 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.438: module A with +//│ ║ l.460: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.434: module A with +//│ ║ l.456: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.430: module A with +//│ ║ l.452: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.426: module A with +//│ ║ l.448: module A with //│ ╙── ^ //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls index b4b21cc984..ad2d6fb9ff 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls @@ -24,7 +24,7 @@ object Lol with :re Lol.x //│ ╔══[ERROR] Object 'Lol' does not contain member 'x' -//│ ║ l.25: Lol.x +//│ ║ l.36: Lol.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' @@ -43,8 +43,8 @@ object Lol with :re Lol.x //│ ╔══[ERROR] Object 'Lol' does not contain member 'x' -//│ ║ l.44: Lol.x -//│ ╙── ^^ +//│ ║ l.113: Lol.x +//│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index 05fe693cbe..0a103e66bc 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -7,7 +7,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -27,7 +27,7 @@ fun foo(x) = let xs = [() => return x + 1] xs.0() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -92,10 +92,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -124,10 +124,10 @@ fun f() = 100 f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -147,7 +147,7 @@ f() fun f() = () => return 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -166,7 +166,7 @@ f()() fun f(): () -> Int = () => return () => 3 f()() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -217,7 +217,7 @@ fun f() = print("Bad") f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index d37fafb8a8..9b3aa39332 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -270,7 +270,7 @@ str //│ case 0: //│ runtime.resumeValue = runtime.safeCall(h22.perform(runtime.Unit)); //│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:154:5", null, null, 1, 1, h22, 0) +//│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:164:5", null, null, 1, 1, h22, 0) //│ } //│ pc = 1; //│ continue main; diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 968b07d41b..0b7d6fe5da 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -17,7 +17,7 @@ fun f() = return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -43,7 +43,7 @@ fun f() = return 3 10 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:241) +//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) //│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. //│ FAILURE: Unexpected exception //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. @@ -126,7 +126,7 @@ let l = () => 4 l() //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.125: return 3 +//│ ║ l.165: return 3 //│ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 43865ff121..ce0fe5e508 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -45,6 +45,15 @@ class A with //│ runtime.safeCall(tmp2.x()) //│ = 2 +:expect 2 +class A(a) with + let x = 2 + fun f() = + fun g() = x + g() +A(2).f() +//│ = 2 + class A with let x = 2 val bruh = () => x diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 6c98e61c57..381a2dcc93 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -481,3 +481,16 @@ fun f(x) = else g g + +// Spread arguments + +:expect [1] +fun f(x) = + fun g(...rest) = + print(x) + rest + let a = g + a(1) +f(2) +//│ > 2 +//│ = [1] diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index e644b39514..7b13a5a0e1 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -12,7 +12,7 @@ fun foo(x, y) = x + y + test M.foo() //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 14 @@ -104,7 +104,7 @@ fun foo(y) = set y = 2 O //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. let o = foo(10) @@ -124,7 +124,7 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. //│ FAILURE: Unexpected compilation error //│ FAILURE LOCATION: lookup_! (Scope.scala:114) @@ -187,7 +187,7 @@ fun foo(x, y) = //│ return tmp //│ }; //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 12 @@ -243,44 +243,7 @@ module M with fun get = x val hi = A() M.hi.get -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, member:foo3 -> foo31, member:foo -> foo4, object:M -> M3, $block$res -> block$res1, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, class:A -> A, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res13, class:Capture$foo -> Capture$foo) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($tmp -> tmp1) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ╟── which references the symbol introduced here -//│ ║ l.242: class A() with -//│ ║ ^^^^^^^^ -//│ ║ l.243: fun get = x -//│ ╙── ^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ at (REPL56:1:612) -//│ at REPL56:1:718 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 :expect 2 module M with @@ -315,31 +278,6 @@ module M with fun get = M.x + x val x = if A() is A then 2 else 3 M.A().get -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res14, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, member:M -> M7, $Term -> Term, c0 -> c0, module:M -> M6, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, class:A -> A2, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, $block$res -> block$res15, member:foo -> foo3, class:M -> M6, object:O -> O, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:M -> M9, module:M -> M8, $block$res -> block$res, member:Capture$foo -> Capture$foo3, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res10, $block$res -> block$res16, $selRes -> selRes4, class:M -> M8, $discarded -> discarded4, $tmp -> tmp1, member:foo3 -> foo31, member:foo -> foo4, member:g$ -> g$, object:M -> M3, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res11, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res12, $block$res -> block$res3, member:M -> M5, member:A -> A1, member:g -> g1, class:A -> A, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, object:M -> M4, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $block$res -> block$res13, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = N -//│ bindings = HashMap(member:M -> M15, module:M -> M14, class:M -> M14, class:A -> A5, $tmp -> tmp3) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut, $tmp -> tmp4) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ╟── which references the symbol introduced here -//│ ║ l.314: class A() with -//│ ║ ^^^^^^^^ -//│ ║ l.315: fun get = M.x + x -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ JS (unsanitized): //│ let M15, tmp3; //│ (class M14 { @@ -367,7 +305,7 @@ M.A().get //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A", []]; //│ }); -//│ scrut = A(); +//│ scrut = M14.A(); //│ if (scrut instanceof M14.A.class) { //│ tmp4 = 2; //│ } else { @@ -381,19 +319,7 @@ M.A().get //│ }); //│ tmp3 = M15.A(); //│ tmp3.get -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ at (REPL68:1:604) -//│ at REPL68:1:800 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ = 4 module M with val x = 2 @@ -418,87 +344,19 @@ module M with fun newB = B() 0 is A M.A(2).newB.newA_B(3).newB.get -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut) -//│ curThis = S of S of class:A -//│ bindings = HashMap(x -> x) -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ╟── which references the symbol introduced here -//│ ║ l.442: data class A(x) with -//│ ║ ^^^^^^^^^ -//│ ║ l.443: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.444: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.445: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.446: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:A -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, member:M -> M15, $runtime -> runtime, module:M -> M14, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, class:A -> A5, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res19, class:M -> M14, $tmp -> tmp3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, member:M -> M17, module:M -> M16, o -> o, class:A -> A6, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:B -> B, member:foo3 -> foo31, member:foo -> foo4, $block$res -> block$res20, class:M -> M16, object:M -> M3, $tmp -> tmp4, $selRes -> selRes7, $discarded -> discarded7, member:B -> B2, member:M -> M19, module:M -> M18, class:A -> A7, $block$res -> block$res11, class:B -> B1, $block$res -> block$res12, member:M -> M5, member:A -> A1, class:A -> A, object:M -> M4, $block$res -> block$res21, class:M -> M18, $tmp -> tmp5, $block$res -> block$res13, $tmp -> tmp6, $selRes -> selRes8, $discarded -> discarded8, $selRes -> selRes9, $discarded -> discarded9, $block$res -> block$res14, $tmp -> tmp, member:M -> M7, module:M -> M6, class:A -> A2, $block$res -> block$res15, class:M -> M6, $selRes -> selRes5, $discarded -> discarded5, member:g -> g, member:M -> M9, module:M -> M8, $block$res -> block$res, member:foo -> foo, object:M -> M, $block$res -> block$res16, class:M -> M8, $tmp -> tmp1, member:g$ -> g$, $block$res -> block$res1, member:A -> A4, member:M -> M11, object:M -> M10, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:A -> A3, class:M -> M1, member:Predef -> Predef, $block$res -> block$res17, $selRes -> selRes6, $discarded -> discarded6, $block$res -> block$res3, member:g -> g1, member:M -> M13, object:M -> M12, member:C -> C1, member:foo -> foo2, class:C -> C, $block$res -> block$res4, $block$res -> block$res18, $tmp -> tmp2, class:Capture$foo -> Capture$foo, member:g$ -> g$1) -//│ curThis = S of S of module:M -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap($scrut -> scrut) -//│ curThis = S of S of class:A -//│ bindings = HashMap(x -> x) -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'A' -//│ ╟── which references the symbol introduced here -//│ ║ l.442: data class A(x) with -//│ ║ ^^^^^^^^^ -//│ ║ l.443: class B() with -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.444: fun get = x -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.445: fun newA_B(y) = A(y) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.446: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: A is not defined -//│ at get newB (REPL74:1:1250) -//│ at REPL74:1:1688 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded +//│ at get newA (REPL74:1:1156) +//│ at get newB (REPL74:1:1303) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) +//│ at get newB (REPL74:1:1314) module M with class A @@ -512,16 +370,16 @@ fun foo(x) = val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. //│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:265) +//│ FAILURE LOCATION: applyValue (Lifter.scala:262) //│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. foo(123) From 1dfd3c9367dac2f10ed920f85f338ce8a52a28bc Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 19 Jan 2026 16:54:13 +0800 Subject: [PATCH 14/18] use aux params for class aux params --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 168 +++++++++++------- hkmc2/shared/src/test/mlscript/HkScratch.mls | 30 +--- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 61 +++---- .../src/test/mlscript/backlog/Lifter.mls | 10 +- .../test/mlscript/handlers/BadHandlers.mls | 2 +- .../src/test/mlscript/handlers/Debugging.mls | 6 +- .../src/test/mlscript/handlers/Effects.mls | 8 +- .../mlscript/handlers/HandlersInClasses.mls | 6 +- .../mlscript/handlers/ManualStackSafety.mls | 13 +- .../mlscript/handlers/RecursiveHandlers.mls | 2 +- .../mlscript/handlers/ReturnInHandler.mls | 4 +- .../src/test/mlscript/lifter/ClassInFun.mls | 70 ++++++-- .../test/mlscript/lifter/ModulesObjects.mls | 57 +++--- 13 files changed, 245 insertions(+), 192 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 42b783020b..694ca624e9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -112,7 +112,7 @@ object Lifter: * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. */ -class Lifter(topLevelBlk: Block)(using State, Raise): +class Lifter(topLevelBlk: Block)(using State, Raise, Config): import Lifter.* extension (l: Local) @@ -340,41 +340,39 @@ class Lifter(topLevelBlk: Block)(using State, Raise): override def applyResult(r: Result)(k: Result => Block): Block = r match // if possible, directly rewrite the call using the efficient version case c @ Call(RefOfBms(l, S(d)), args) => - val newCall: Call = ctx.rewrittenScopes.get(d) match - case None => c - case Some(r) => - def join = - resolveDefnRef(l, d, r) match - case Some(value) => c.copy(fun = value)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) - case None => c - r match - // function call - case f: LiftedFunc => f.rewriteCall(c) - // ctor call (without using `new`) - case ctor: RewrittenClassCtor => ctor.getRewrittenCls match - case cls: LiftedClass => - cls.rewriteCall(c) - case _ => join - case _ => join - applyArgs(newCall.args): newArgs => - if (newCall.args is newArgs) && (c is newCall) then k(newCall) - else k(Call(newCall.fun, newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) + applyArgs(args): newArgs => + def join1: Call = + if args is newArgs then c + else c.copy(args = newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) + val newRes: Call = ctx.rewrittenScopes.get(d) match + case N => join1 + case S(r) => + def join2: Call = + resolveDefnRef(l, d, r) match + case Some(value) => c.copy(fun = value, args = newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) + case None => join1 + r match + // function call + case f: LiftedFunc => f.rewriteCall(c, newArgs) + // ctor call (without using `new`) + case ctor: RewrittenClassCtor => ctor.getRewrittenCls match + case cls: LiftedClass => + cls.rewriteCall(c, newArgs) + case _ => join2 + case _ => join2 + k(newRes) case inst @ Instantiate(mut, RefOfBms(l, S(d)), args) => - // It is VERY IMPORTANT that we rewrite it like this and not using super.applyResult. - // The reason is that Instantiate is disambiguated using the class's InnerSymbol, which is also - // used to represent the class's `this`. super.applyResult would apply super.applyPath on the - // disambiguated BMS ref, which would replace it with the InnerSymbol, since the class scoped object - // adds `this -> this` to the symbols map. - val newInst = ctx.rewrittenScopes.get(d) match - case S(c: LiftedClass) => c.rewriteInstantiate(inst) - case S(r) => resolveDefnRef(l, d, r) match - case Some(value) => Instantiate(inst.mut, value, inst.args) - case None => inst - case N => inst - - applyArgs(newInst.args): newArgs => - if (newInst.args is newArgs) && (newInst is inst) then k(newInst) - else k(Instantiate(newInst.mut, newInst.cls, newArgs)) + applyArgs(args): newArgs => + def join = + if args is newArgs then inst + else inst.copy(args = newArgs) + val res = ctx.rewrittenScopes.get(d) match + case N => join + case S(c: LiftedClass) => c.rewriteInstantiate(inst, newArgs) + case S(r) => resolveDefnRef(l, d, r) match + case Some(value) => Instantiate(inst.mut, value, newArgs) + case None => join + k(res) case _ => super.applyResult(r)(k) // extract the call @@ -1017,12 +1015,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise): bod )(false) - def rewriteCall(c: Call)(using ctx: LifterCtxNew): Call = + def rewriteCall(c: Call, args: List[Arg])(using ctx: LifterCtxNew): Call = if isTrivial then c else Call( Value.Ref(mainSym, S(mainDsym)), - formatArgs ::: c.args + formatArgs ::: args )( isMlsFun = true, mayRaiseEffects = c.mayRaiseEffects, @@ -1087,21 +1085,73 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val cls = obj.cls - def rewriteInstantiate(inst: Instantiate): Instantiate = - if isTrivial then inst - else - Instantiate( - inst.mut, - Value.Ref(cls.sym, S(cls.isym)), - formatArgs ::: inst.args - ) + val flattenedSym = BlockMemberSymbol(obj.cls.sym.nme + "$", Nil, true) + val flattenedDSym = TermSymbol.fromFunBms(flattenedSym, N) + + def mkFlattenedDefn: Opt[FunDefn] = + if isTrivial then return N + val auxSyms = auxParams.map(p => VarSymbol(Tree.Ident(p.sym.nme))) + val main = obj.cls.paramsOpt match + case Some(value) => dupParamList(value) + case None => obj.cls.auxParams.headOption match + case Some(value) => dupParamList(value) + case None => PlainParamList(Nil) + val mainSyms = main.params.map(_.sym) + val restSym = main.restParam.map(_.sym) + val argList1_ = (restSym match + case Some(value) => mainSyms.appended(value) + case None => mainSyms + ).map(s => s.asPath.asArg) + val argList2_ = auxSyms.map(s => s.asPath.asArg) + + val clsIsParamless = cls.paramsOpt.isEmpty && cls.auxParams.length == 0 + + val argList1 = + if cls.paramsOpt.isEmpty && cls.auxParams.length == 0 then argList2_ + else argList1_ + val argList2 = argList2_ + + val isMut = VarSymbol(Tree.Ident("isMut")) + val params = ParamList( + ParamListFlags.empty, + Param.simple(isMut) :: auxSyms.map(Param.simple(_)) ::: main.params, + main.restParam + ) + val tmp = TempSymbol(N) + val ref = Value.Ref(obj.cls.sym, S(obj.cls.isym)) + val instMut = Assign(tmp, Instantiate(true, ref, argList1), End()) + val inst = Assign(tmp, Instantiate(false, ref, argList1), End()) + val ret = + if clsIsParamless then Return(tmp.asPath, false) + else Return(Call(tmp.asPath, argList2)(true, config.checkInstantiateEffect, false), false) + val bod = Scoped(Set(tmp), Match( + isMut.asPath, + Case.Lit(Tree.BoolLit(true)) -> instMut :: Nil, + S(inst), + ret + )) + + S(FunDefn(N, flattenedSym, flattenedDSym, params :: Nil, bod)(false)) + - def rewriteCall(c: Call)(using ctx: LifterCtxNew): Call = - if isTrivial then c + def rewriteInstantiate(inst: Instantiate, args: List[Arg]): Result = + if isTrivial then + if inst.args is args then inst + else inst.copy(args = args) + else + Call( + Value.Ref(flattenedSym, S(flattenedDSym)), + Value.Lit(Tree.BoolLit(inst.mut)).asArg :: formatArgs ::: args + )(true, config.checkInstantiateEffect, false) + + def rewriteCall(c: Call, args: List[Arg])(using ctx: LifterCtxNew): Call = + if isTrivial then + if c.args is args then c + else c.copy(args = args)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) else Call( - Value.Ref(cls.sym, S(cls.ctorSym.get)), - formatArgs ::: c.args + Value.Ref(flattenedSym, S(flattenedDSym)), + Value.Lit(Tree.BoolLit(false)).asArg :: formatArgs ::: args )( isMlsFun = true, mayRaiseEffects = c.mayRaiseEffects, @@ -1126,17 +1176,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise): val (vs, ts) = capSymsMap_(sym) Assign(ts, vs.asPath, acc) - val (newPlist, newAuxList) = cls.paramsOpt match - case Some(plist) => - ( - S(plist.copy(params = auxParams ::: plist.params)), - cls.auxParams - ) - case None => - ( - N, - PlainParamList(auxParams) :: cls.auxParams - ) + val newAuxList = + if isTrivial then cls.auxParams + else PlainParamList(auxParams) :: cls.auxParams val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( @@ -1145,10 +1187,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise): preCtor = rewrittenPrector, privateFields = captureSym :: obj.cls.privateFields, methods = newMtds, - paramsOpt = newPlist, auxParams = newAuxList ) - LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) + val extrasDefns = rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras + mkFlattenedDefn match + case Some(value) => LifterResult(newCls, value :: extrasDefns) + case None => LifterResult(newCls, extrasDefns) private def createRewritten[T](s: TScopeNode[T])(using ctx: LifterCtxNew): RewrittenScope[T] = s.obj match case _: ScopedObject.Top => lastWords("tried to rewrite the top-level scope") diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 5365663ac0..b09a983866 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -8,32 +8,6 @@ :d :todo +class Eff +//│ Elab: { Cls Eff { }; } -:sjs -class A with - let y = 2 - fun f() = - set y = 3 -let a = new A -a.f() -//│ Elab: { Cls A { let term:class:A‹688›.y‹690›; term:class:A‹688›.y‹690› = 2; method fun member:f‹686›() = term:class:A‹688›.y‹690›#666 := 3; }; let a‹694›; a‹694› = new member:A‹687›#666; a‹694›#666.f() } -//│ JS (unsanitized): -//│ let A1, a; -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() { -//│ this.#y = 2; -//│ } -//│ #y; -//│ f() { -//│ this.#y = 3; -//│ return runtime.Unit -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); -//│ a = globalThis.Object.freeze(new A1()); -//│ runtime.safeCall(a.f()) -//│ a = A diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 2cc21dc38b..64513bf5ce 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,47 +9,34 @@ :todo :noSanityCheck -:sjs + +fun test() = + class A with + fun get = 2 + class B() extends A + B().get +test() +//│ Elab: { fun member:test‹873›() = { Cls A { method fun member:get‹870› = 2; }; Cls BParamList(‹›,List(),None) { }; member:B‹872›#666().get }; member:test‹873›#666() } +//│ = 2 + :lift -fun f = - module M with - fun foo() = 2 - fun bar = M.foo - bar -//│ Elab: { fun member:f‹873› = { Mod M { fun member:foo‹870›() = 2; }; fun member:bar‹871› = member:M‹872›#666.foo‹member:foo‹870››; member:bar‹871›#666 }; } -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.15: module M with -//│ ╙── ^ +:sjs +fun f(x) = + fun g() = x + fun h(a) = a() + h(g) +//│ Elab: { fun member:f‹893›(x‹894›) = { fun member:g‹891›() = x‹894›#666; fun member:h‹892›(a‹899›) = a‹899›#666(); member:h‹892›#666(member:g‹891›#666) }; } //│ JS (unsanitized): -//│ let bar, f, bar$; -//│ bar$ = function bar$(M1) { +//│ let g, h, f, g$; +//│ g$ = function g$(x) { //│ return () => { -//│ return bar(M1) +//│ return g(x) //│ } //│ }; -//│ bar = function bar(M1) { -//│ return M1.foo +//│ g = function g(x) { +//│ return x //│ }; -//│ f = function f() { -//│ let M1, tmp; -//│ (class M { -//│ static { -//│ M1 = this -//│ } -//│ constructor() { -//│ runtime.Unit; -//│ } -//│ static #M_mod$cap; -//│ static foo() { -//│ return 2 -//│ } -//│ #M$cap; -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "M"]; -//│ }); -//│ tmp = bar(M1); -//│ return tmp +//│ h = function h(a) { +//│ return runtime.safeCall(a()) //│ }; -//│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.15: module M with -//│ ╙── ^ +//│ f = function f(x) { let g$here; g$here = g$(x); return h(g) }; diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 7b451d8a61..e9be9caecb 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -149,13 +149,13 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.159: module M with +//│ ║ l.148: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' //│ ╟── which references the symbol introduced here -//│ ║ l.159: module M with +//│ ║ l.148: module M with //│ ║ ^^^^^^ -//│ ║ l.160: fun getB() = x +//│ ║ l.149: fun getB() = x //│ ╙── ^^^^^^^^^^^^^^^^^^ :expect 2 @@ -172,7 +172,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.182: class Bar extends Foo +//│ ║ l.171: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. @@ -189,7 +189,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.197: module M with +//│ ║ l.186: module M with //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls index a1e1200c77..0c5bc7786c 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/BadHandlers.mls @@ -62,7 +62,7 @@ object Foo with :re Foo.x //│ ╔══[ERROR] Object 'Foo' does not contain member 'x' -//│ ║ l.86: Foo.x +//│ ║ l.63: Foo.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index 174af5807e..2fb2bcf659 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -158,8 +158,6 @@ module Test with fun main()(using Debugger) = test(12) + test(34) -// Currently this test fails due to lifter issue. Commenting out :lift at the top of this file will make this work. -// The lifter currently does not correctly consider the member variable as a local that could be captured. let res = handle h = Debugger with fun break(payload)(resume) = resume @@ -199,8 +197,8 @@ fun f() = f() //│ > Stack Trace: -//│ > at f (Debugging.mls:193:3) with locals: j=200 +//│ > at f (Debugging.mls:191:3) with locals: j=200 //│ > Stack Trace: -//│ > at f (Debugging.mls:195:3) +//│ > at f (Debugging.mls:193:3) //│ > Stack Trace: //│ > at tail position diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 7e9f9bce9e..4eb93ce2b4 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -443,16 +443,16 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.460: module A with +//│ ║ l.438: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.456: module A with +//│ ║ l.434: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.452: module A with +//│ ║ l.430: module A with //│ ╙── ^ //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.448: module A with +//│ ║ l.426: module A with //│ ╙── ^ //│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls index ad2d6fb9ff..b4b21cc984 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls @@ -24,7 +24,7 @@ object Lol with :re Lol.x //│ ╔══[ERROR] Object 'Lol' does not contain member 'x' -//│ ║ l.36: Lol.x +//│ ║ l.25: Lol.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' @@ -43,8 +43,8 @@ object Lol with :re Lol.x //│ ╔══[ERROR] Object 'Lol' does not contain member 'x' -//│ ║ l.113: Lol.x -//│ ╙── ^^ +//│ ║ l.44: Lol.x +//│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls index a05382e0cd..73f322a0bd 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls @@ -63,7 +63,7 @@ fun test(n, stackLimit) = set stackDepth = 0 ans //│ JS (unsanitized): -//│ let b, a, b1, a1, mkrec, selfApp, StackDelay1, test, handleBlock$, Handler$h$perform, Handler$h$1, Capture$scope01, handleBlock$$, a$, mkrec$, selfApp$, b$, a$1, b$1, Handler$h$perform$; +//│ let b, a, b1, a1, mkrec, selfApp, StackDelay1, test, handleBlock$, Handler$h$perform, Handler$h$1, Capture$scope01, handleBlock$$, Handler$h$$, a$, mkrec$, selfApp$, b$, a$1, b$1, Handler$h$perform$; //│ (class Capture$scope0 { //│ static { //│ Capture$scope01 = this @@ -132,6 +132,15 @@ fun test(n, stackLimit) = //│ break; //│ } //│ }; +//│ Handler$h$$ = function Handler$h$$(isMut, scope0$cap) { +//│ let tmp; +//│ if (isMut === true) { +//│ tmp = new Handler$h$1(scope0$cap); +//│ } else { +//│ tmp = globalThis.Object.freeze(new Handler$h$1(scope0$cap)); +//│ } +//│ return tmp +//│ }; //│ selfApp$ = function selfApp$(scope0$cap, stackLimit, h) { //│ return (f) => { //│ return selfApp(scope0$cap, stackLimit, h, f) @@ -565,7 +574,7 @@ fun test(n, stackLimit) = //│ scope0$cap = new Capture$scope01(stackDepth, stackOffset); //│ scope0$cap.stackDepth$0 = 0; //│ scope0$cap.stackOffset$1 = 0; -//│ h = new Handler$h$1(scope0$cap); +//│ h = Handler$h$$(true, scope0$cap); //│ handleBlock$$here = handleBlock$$(scope0$cap, n, stackLimit, h); //│ runtime.resumeValue = runtime.enterHandleBlock(h, handleBlock$$here); //│ if (runtime.curEffect !== null) { diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 9b3aa39332..d37fafb8a8 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -270,7 +270,7 @@ str //│ case 0: //│ runtime.resumeValue = runtime.safeCall(h22.perform(runtime.Unit)); //│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:164:5", null, null, 1, 1, h22, 0) +//│ return runtime.unwind(handleBlock$9, 1, "RecursiveHandlers.mls:154:5", null, null, 1, 1, h22, 0) //│ } //│ pc = 1; //│ continue main; diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 0b7d6fe5da..87d4dc46c7 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -69,7 +69,7 @@ f() //│ _2 = Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res2, $tmp -> tmp, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res1, member:Predef -> Predef) +//│ bindings = HashMap($block$res -> block$res2, $runtime -> runtime, $tmp -> tmp, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res1, member:Predef -> Predef) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' //│ ║ l.62: f() //│ ║ ^ @@ -126,7 +126,7 @@ let l = () => 4 l() //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.165: return 3 +//│ ║ l.125: return 3 //│ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index ee49179ad4..514631c452 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -90,7 +90,7 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f6, tmp5, Capture$scope01; +//│ let Bad1, Good1, f6, tmp5, Capture$scope01, Bad$, Good$; //│ (class Capture$scope0 { //│ static { //│ Capture$scope01 = this @@ -102,17 +102,40 @@ f().foo() //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Capture$scope0"]; //│ }); -//│ Good1 = function Good(scope0$cap, x, y) { -//│ return globalThis.Object.freeze(new Good.class(scope0$cap, x, y)); +//│ Good$ = function Good$(isMut, scope0$cap, x, y) { +//│ let tmp6; +//│ if (isMut === true) { +//│ tmp6 = new Good1.class(); +//│ } else { +//│ tmp6 = globalThis.Object.freeze(new Good1.class()); +//│ } +//│ return tmp6(scope0$cap, x, y) +//│ }; +//│ Bad$ = function Bad$(isMut, scope0$cap) { +//│ let tmp6; +//│ if (isMut === true) { +//│ tmp6 = new Bad1.class(); +//│ } else { +//│ tmp6 = globalThis.Object.freeze(new Bad1.class()); +//│ } +//│ return tmp6(scope0$cap) +//│ }; +//│ Good1 = function Good() { +//│ return (scope0$cap, x, y) => { +//│ return globalThis.Object.freeze(new Good.class()(scope0$cap, x, y)); +//│ } //│ }; //│ (class Good { //│ static { //│ Good1.class = this //│ } -//│ constructor(scope0$cap, x, y) { -//│ this.scope0$cap = scope0$cap; -//│ this.x = x; -//│ this.y = y; +//│ constructor() { +//│ return (scope0$cap, x, y) => { +//│ this.scope0$cap = scope0$cap; +//│ this.x = x; +//│ this.y = y; +//│ return this; +//│ } //│ } //│ #Good$cap; //│ foo() { @@ -123,17 +146,22 @@ f().foo() //│ return tmp7 + this.scope0$cap.w$1 //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Good", [null, null, null]]; +//│ static [definitionMetadata] = ["class", "Good", []]; //│ }); -//│ Bad1 = function Bad(scope0$cap) { -//│ return globalThis.Object.freeze(new Bad.class(scope0$cap)); +//│ Bad1 = function Bad() { +//│ return (scope0$cap) => { +//│ return globalThis.Object.freeze(new Bad.class()(scope0$cap)); +//│ } //│ }; //│ (class Bad { //│ static { //│ Bad1.class = this //│ } -//│ constructor(scope0$cap) { -//│ this.scope0$cap = scope0$cap; +//│ constructor() { +//│ return (scope0$cap) => { +//│ this.scope0$cap = scope0$cap; +//│ return this; +//│ } //│ } //│ #Bad$cap; //│ foo() { @@ -141,7 +169,7 @@ f().foo() //│ return runtime.Unit //│ } //│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Bad", [null]]; +//│ static [definitionMetadata] = ["class", "Bad", []]; //│ }); //│ f6 = function f() { //│ let x, y, z, w, tmp6, tmp7, scope0$cap; @@ -150,9 +178,9 @@ f().foo() //│ y = 10; //│ scope0$cap.z$0 = 10; //│ scope0$cap.w$1 = 1000; -//│ tmp6 = Bad1(scope0$cap); +//│ tmp6 = Bad$(false, scope0$cap); //│ tmp7 = tmp6.foo(); -//│ return Good1(scope0$cap, x, y) +//│ return Good$(false, scope0$cap, x, y) //│ }; //│ tmp5 = f6(); //│ runtime.safeCall(tmp5.foo()) @@ -173,8 +201,8 @@ let b = a.newA() b.foo() a.getX() //│ = 2 -//│ a = A(_, _) -//│ b = A(_, _) +//│ a = A() +//│ b = A() // handler @@ -229,3 +257,11 @@ fun test(f) = f(x) A(1) +// Check that the extra params are not printed +:expect "A(2)" +fun f(x) = + data class A(y) with + fun hi = x + y + A(2).toString() +f(0) +//│ = "A(2)" diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 7b13a5a0e1..98fbe6478b 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -30,7 +30,16 @@ fun foo(y) = (new M).foo() foo(10) //│ JS (unsanitized): -//│ let M2, foo1; +//│ let M2, foo1, M$; +//│ M$ = function M$(isMut, y) { +//│ let tmp; +//│ if (isMut === true) { +//│ tmp = new M2(y); +//│ } else { +//│ tmp = globalThis.Object.freeze(new M2(y)); +//│ } +//│ return tmp +//│ }; //│ (class M1 { //│ static { //│ M2 = this @@ -46,18 +55,14 @@ foo(10) //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); -//│ foo1 = function foo(y) { -//│ let tmp; -//│ tmp = globalThis.Object.freeze(new M2(y)); -//│ return tmp.foo() -//│ }; +//│ foo1 = function foo(y) { let tmp; tmp = M$(false, y); return tmp.foo() }; //│ foo1(10) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M1.foo (REPL16:1:283) -//│ at foo (REPL16:1:602) -//│ at REPL16:1:643 +//│ at M1.foo (REPL16:1:540) +//│ at foo (REPL16:1:855) +//│ at REPL16:1:896 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) //│ at bound (node:domain:433:15) @@ -138,7 +143,7 @@ fun foo(x, y) = //│ parent = S of Scope: //│ parent = N //│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap(member:Capture$foo -> Capture$foo1, $block$res -> block$res1, $runtime -> runtime, $definitionMetadata -> definitionMetadata, cc -> cc, $prettyPrint -> prettyPrint, $Term -> Term, c0 -> c0, $Block -> Block, c1 -> c1, $Shape -> Shape, $block$res -> block$res2, $block$res -> block$res5, member:M -> M2, $selRes -> selRes, member:foo -> foo1, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, class:M -> M1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, member:Predef -> Predef, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, object:O -> O, $block$res -> block$res3, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:C -> C1, $block$res -> block$res, member:foo -> foo2, member:Capture$foo -> Capture$foo3, class:C -> C, member:foo -> foo, o -> o, object:M -> M, $block$res -> block$res9, $block$res -> block$res4, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, class:Capture$foo -> Capture$foo) +//│ bindings = HashMap($block$res -> block$res4, class:Capture$foo -> Capture$foo, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, member:Capture$foo -> Capture$foo1, $Block -> Block, $Shape -> Shape, member:C$ -> C$, cc -> cc, c0 -> c0, c1 -> c1, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, $block$res -> block$res, object:O -> O, member:foo -> foo, object:M -> M, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, o -> o, $block$res -> block$res1, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:M -> M1, member:Predef -> Predef, $block$res -> block$res3, member:M$ -> M$, member:C -> C1, member:foo -> foo2, class:C -> C) //│ curThis = N //│ bindings = HashMap(object:M -> M3, member:foo3 -> foo31, member:foo -> foo4) //│ curThis = S of N @@ -146,16 +151,16 @@ fun foo(x, y) = //│ curThis = N //│ bindings = HashMap() //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' -//│ ║ l.124: fun foo3 = M.foo2() +//│ ║ l.129: fun foo3 = M.foo2() //│ ║ ^ //│ ╟── which references the symbol introduced here -//│ ║ l.120: object M with +//│ ║ l.125: object M with //│ ║ ^^^^^^ -//│ ║ l.121: fun foo2() = +//│ ║ l.126: fun foo2() = //│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.122: set y = 2 +//│ ║ l.127: set y = 2 //│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.123: x + y +//│ ║ l.128: x + y //│ ╙── ^^^^^^^^^^^ //│ JS (unsanitized): //│ let foo31, foo4; @@ -220,7 +225,7 @@ A(2).getA() //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'getB') -//│ at A.getA (REPL50:1:899) +//│ at A.getA (REPL50:1:1161) //│ at REPL53:1:83 //│ at ContextifyScript.runInThisContext (node:vm:137:12) //│ at REPLServer.defaultEval (node:repl:562:24) @@ -347,16 +352,16 @@ M.A(2).newB.newA_B(3).newB.get //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded -//│ at get newA (REPL74:1:1156) -//│ at get newB (REPL74:1:1303) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) -//│ at get newB (REPL74:1:1314) +//│ at get newA (REPL74:1:1666) +//│ at get newB (REPL74:1:1820) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) +//│ at get newB (REPL74:1:1831) module M with class A From 4119cc5c4bfddb843348b9ff0889b99ab00ab50c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 19 Jan 2026 16:57:09 +0800 Subject: [PATCH 15/18] update test --- .../mlscript/handlers/ManualStackSafety.mls | 531 +----------------- .../test/mlscript/handlers/NestedHandlers.mls | 310 ---------- 2 files changed, 1 insertion(+), 840 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls index 73f322a0bd..de1b403d98 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ManualStackSafety.mls @@ -8,7 +8,7 @@ // * It is notably interesting in that it demonstrates the ability to preserve tail calls. // * The original function can be found in `hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls` -:sjs + fun test(n, stackLimit) = let stackDepth = 0 let stackOffset = 0 @@ -62,535 +62,6 @@ fun test(n, stackLimit) = let ans = fact(n) set stackDepth = 0 ans -//│ JS (unsanitized): -//│ let b, a, b1, a1, mkrec, selfApp, StackDelay1, test, handleBlock$, Handler$h$perform, Handler$h$1, Capture$scope01, handleBlock$$, Handler$h$$, a$, mkrec$, selfApp$, b$, a$1, b$1, Handler$h$perform$; -//│ (class Capture$scope0 { -//│ static { -//│ Capture$scope01 = this -//│ } -//│ constructor(stackDepth$0, stackOffset$1) { -//│ this.stackOffset$1 = stackOffset$1; -//│ this.stackDepth$0 = stackDepth$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Capture$scope0"]; -//│ }); -//│ Handler$h$perform$ = function Handler$h$perform$(scope0$cap) { -//│ return (resume) => { -//│ return Handler$h$perform(scope0$cap, resume) -//│ } -//│ }; -//│ Handler$h$perform = function Handler$h$perform(scope0$cap, resume) { -//│ let curOffset, tmp, tmp1, tmp2, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ saveOffset = runtime.resumeIdx; -//│ curOffset = runtime.resumeArr.at(saveOffset); -//│ saveOffset = saveOffset + 1; -//│ tmp = runtime.resumeArr.at(saveOffset); -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ curOffset = scope0$cap.stackOffset$1; -//│ scope0$cap.stackOffset$1 = scope0$cap.stackDepth$0; -//│ runtime.resumeValue = globalThis.console.log("resuming at offset:", curOffset); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h$perform, 3, "ManualStackSafety.mls:21:7", null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) -//│ } -//│ pc = 3; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp2 = runtime.resumeValue; -//│ scope0$cap.stackOffset$1 = curOffset; -//│ return tmp; -//│ break; -//│ case 2: -//│ tmp = runtime.resumeValue; -//│ runtime.resumeValue = globalThis.console.log("finished at offset:", curOffset); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h$perform, 1, "ManualStackSafety.mls:23:7", null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 3: -//│ tmp1 = runtime.resumeValue; -//│ runtime.resumeValue = runtime.safeCall(resume()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(Handler$h$perform, 2, null, null, null, 1, 2, scope0$cap, resume, 2, curOffset, tmp) -//│ } -//│ pc = 2; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ Handler$h$$ = function Handler$h$$(isMut, scope0$cap) { -//│ let tmp; -//│ if (isMut === true) { -//│ tmp = new Handler$h$1(scope0$cap); -//│ } else { -//│ tmp = globalThis.Object.freeze(new Handler$h$1(scope0$cap)); -//│ } -//│ return tmp -//│ }; -//│ selfApp$ = function selfApp$(scope0$cap, stackLimit, h) { -//│ return (f) => { -//│ return selfApp(scope0$cap, stackLimit, h, f) -//│ } -//│ }; -//│ b$1 = function b$(scope0$cap, stackLimit, h, self) { -//│ return (y) => { -//│ return b(scope0$cap, stackLimit, h, self, y) -//│ } -//│ }; -//│ b = function b(scope0$cap, stackLimit, h, self, y) { -//│ let scrut, tmp, res, tmp1, tmp2, tmp3, tmp4, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ saveOffset = runtime.resumeIdx; -//│ tmp = runtime.resumeArr.at(saveOffset); -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp1 = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp1 >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b, 3, "ManualStackSafety.mls:35:55", null, null, 1, 5, scope0$cap, stackLimit, h, self, y, 1, tmp) -//│ } -//│ pc = 3; -//│ continue main -//│ } else { -//│ tmp2 = runtime.Unit; -//│ } -//│ pc = 2; -//│ continue main; -//│ break; -//│ case 1: -//│ res = runtime.resumeValue; -//│ scope0$cap.stackDepth$0 = tmp; -//│ tmp4 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp4; -//│ return runtime.safeCall(res(y)); -//│ break; -//│ case 2: -//│ tmp = scope0$cap.stackDepth$0; -//│ tmp3 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp3; -//│ runtime.resumeValue = selfApp(scope0$cap, stackLimit, h, self); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b, 1, null, null, null, 1, 5, scope0$cap, stackLimit, h, self, y, 1, tmp) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 3: -//│ tmp2 = runtime.resumeValue; -//│ pc = 2; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ a$1 = function a$(scope0$cap, stackLimit, h, g) { -//│ return (self) => { -//│ return a(scope0$cap, stackLimit, h, g, self) -//│ } -//│ }; -//│ a = function a(scope0$cap, stackLimit, h, g, self) { -//│ let scrut, tmp, tmp1, tmp2, b$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(a, 2, "ManualStackSafety.mls:33:53", null, null, 1, 5, scope0$cap, stackLimit, h, g, self, 0) -//│ } -//│ pc = 2; -//│ continue main -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp2 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp2; -//│ b$here = b$1(scope0$cap, stackLimit, h, self); -//│ return runtime.safeCall(g(b$here)); -//│ break; -//│ case 2: -//│ tmp1 = runtime.resumeValue; -//│ pc = 1; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ mkrec$ = function mkrec$(scope0$cap, stackLimit, h) { -//│ return (g) => { -//│ return mkrec(scope0$cap, stackLimit, h, g) -//│ } -//│ }; -//│ b$ = function b$(scope0$cap, stackLimit, h, self) { -//│ return (x) => { -//│ return b1(scope0$cap, stackLimit, h, self, x) -//│ } -//│ }; -//│ b1 = function b(scope0$cap, stackLimit, h, self, x) { -//│ let scrut, scrut1, tmp, prev, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ saveOffset = runtime.resumeIdx; -//│ tmp = runtime.resumeArr.at(saveOffset); -//│ saveOffset = saveOffset + 1; -//│ prev = runtime.resumeArr.at(saveOffset); -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp1 = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp1 >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b1, 7, "ManualStackSafety.mls:50:55", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) -//│ } -//│ pc = 7; -//│ continue main -//│ } else { -//│ tmp2 = runtime.Unit; -//│ } -//│ pc = 6; -//│ continue main; -//│ break; -//│ case 5: -//│ scrut1 = runtime.resumeValue; -//│ if (scrut1 === true) { -//│ return 1 -//│ } else { -//│ runtime.resumeValue = globalThis.console.log(scope0$cap.stackDepth$0, scope0$cap.stackOffset$1); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b1, 4, "ManualStackSafety.mls:52:11", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) -//│ } -//│ pc = 4; -//│ continue main -//│ } -//│ break; -//│ case 1: -//│ tmp6 = runtime.resumeValue; -//│ return x * prev; -//│ break; -//│ case 6: -//│ runtime.resumeValue = Predef.equals(x, 0); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b1, 5, "ManualStackSafety.mls:51:14", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) -//│ } -//│ pc = 5; -//│ continue main; -//│ break; -//│ case 2: -//│ prev = runtime.resumeValue; -//│ scope0$cap.stackDepth$0 = tmp; -//│ runtime.resumeValue = globalThis.console.log("resumed:", x); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b1, 1, "ManualStackSafety.mls:57:11", null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 7: -//│ tmp2 = runtime.resumeValue; -//│ pc = 6; -//│ continue main; -//│ break; -//│ case 3: -//│ runtime.resumeValue = runtime.safeCall(self(tmp5)); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(b1, 2, null, null, null, 1, 5, scope0$cap, stackLimit, h, self, x, 2, tmp, prev) -//│ } -//│ pc = 2; -//│ continue main; -//│ break; -//│ case 4: -//│ tmp3 = runtime.resumeValue; -//│ tmp = scope0$cap.stackDepth$0; -//│ tmp4 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp4; -//│ tmp5 = x - 1; -//│ pc = 3; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ a$ = function a$(scope0$cap, stackLimit, h) { -//│ return (self) => { -//│ return a1(scope0$cap, stackLimit, h, self) -//│ } -//│ }; -//│ selfApp = function selfApp(scope0$cap, stackLimit, h, f) { -//│ let scrut, tmp, tmp1, tmp2, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(selfApp, 2, "ManualStackSafety.mls:27:51", null, null, 1, 4, scope0$cap, stackLimit, h, f, 0) -//│ } -//│ pc = 2; -//│ continue main -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp2 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp2; -//│ return runtime.safeCall(f(f)); -//│ break; -//│ case 2: -//│ tmp1 = runtime.resumeValue; -//│ pc = 1; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ mkrec = function mkrec(scope0$cap, stackLimit, h, g) { -//│ let scrut, tmp, tmp1, tmp2, a$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(mkrec, 2, "ManualStackSafety.mls:31:51", null, null, 1, 4, scope0$cap, stackLimit, h, g, 0) -//│ } -//│ pc = 2; -//│ continue main -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp2 = scope0$cap.stackDepth$0 + 1; -//│ scope0$cap.stackDepth$0 = tmp2; -//│ a$here = a$1(scope0$cap, stackLimit, h, g); -//│ return selfApp(scope0$cap, stackLimit, h, a$here); -//│ break; -//│ case 2: -//│ tmp1 = runtime.resumeValue; -//│ pc = 1; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ a1 = function a(scope0$cap, stackLimit, h, self) { -//│ let scrut, tmp, tmp1, b$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ tmp = scope0$cap.stackDepth$0 - scope0$cap.stackOffset$1; -//│ scrut = tmp >= stackLimit; -//│ if (scrut === true) { -//│ runtime.resumeValue = runtime.safeCall(h.perform()); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(a1, 2, "ManualStackSafety.mls:48:53", null, null, 1, 4, scope0$cap, stackLimit, h, self, 0) -//│ } -//│ pc = 2; -//│ continue main -//│ } else { -//│ tmp1 = runtime.Unit; -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ b$here = b$(scope0$cap, stackLimit, h, self); -//│ return b$here; -//│ break; -//│ case 2: -//│ tmp1 = runtime.resumeValue; -//│ pc = 1; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }; -//│ handleBlock$$ = (undefined, function (scope0$cap, n, stackLimit, h) { -//│ return () => { -//│ return handleBlock$(scope0$cap, n, stackLimit, h) -//│ } -//│ }); -//│ (class StackDelay { -//│ static { -//│ StackDelay1 = this -//│ } -//│ constructor() {} -//│ #StackDelay$cap; -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "StackDelay"]; -//│ }); -//│ (class Handler$h$ extends StackDelay1 { -//│ static { -//│ Handler$h$1 = this -//│ } -//│ constructor(scope0$cap) { -//│ let tmp; -//│ tmp = super(); -//│ if (runtime.curEffect !== null) { -//│ tmp = runtime.illegalEffect("in a constructor"); -//│ } -//│ tmp; -//│ this.scope0$cap = scope0$cap; -//│ } -//│ #Handler$h$$cap; -//│ perform() { -//│ let Handler$h$perform$here; -//│ Handler$h$perform$here = Handler$h$perform$(this.scope0$cap); -//│ return runtime.mkEffect(this, Handler$h$perform$here) -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "Handler$h$"]; -//│ }); -//│ handleBlock$ = (undefined, function (scope0$cap, n, stackLimit, h) { -//│ let fact, ans, a$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ a$here = a$(scope0$cap, stackLimit, h); -//│ runtime.resumeValue = mkrec(scope0$cap, stackLimit, h, a$here); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$, 2, null, null, null, 1, 4, scope0$cap, n, stackLimit, h, 0) -//│ } -//│ pc = 2; -//│ continue main; -//│ break; -//│ case 1: -//│ ans = runtime.resumeValue; -//│ scope0$cap.stackDepth$0 = 0; -//│ return ans; -//│ break; -//│ case 2: -//│ fact = runtime.resumeValue; -//│ scope0$cap.stackDepth$0 = 1; -//│ runtime.resumeValue = runtime.safeCall(fact(n)); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 4, scope0$cap, n, stackLimit, h, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ } -//│ break; -//│ } -//│ }); -//│ test = function test(n, stackLimit) { -//│ let stackDepth, stackOffset, h, tmp, scope0$cap, handleBlock$$here, pc; -//│ if (runtime.resumePc === -1) { -//│ pc = 0; -//│ } else { -//│ let saveOffset; -//│ pc = runtime.resumePc; -//│ runtime.resumePc = -1; -//│ } -//│ main: while (true) { -//│ switch (pc) { -//│ case 0: -//│ scope0$cap = new Capture$scope01(stackDepth, stackOffset); -//│ scope0$cap.stackDepth$0 = 0; -//│ scope0$cap.stackOffset$1 = 0; -//│ h = Handler$h$$(true, scope0$cap); -//│ handleBlock$$here = handleBlock$$(scope0$cap, n, stackLimit, h); -//│ runtime.resumeValue = runtime.enterHandleBlock(h, handleBlock$$here); -//│ if (runtime.curEffect !== null) { -//│ return runtime.unwind(test, 1, null, null, null, 1, 2, n, stackLimit, 0) -//│ } -//│ pc = 1; -//│ continue main; -//│ break; -//│ case 1: -//│ tmp = runtime.resumeValue; -//│ return tmp; -//│ break; -//│ } -//│ break; -//│ } -//│ }; :expect 3628800 test(10, 100) diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 5add78aec1..392d5fb2a6 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -9,7 +9,6 @@ let id = 0 class MaybeStop with fun f(x: Bool): () -:slot fun handleEffects(g) = handle h1 = MaybeStop with fun f(x)(k) = @@ -34,315 +33,6 @@ fun handleEffects(g) = print("h2 end " + String(cur)) result g(h1, h2) -//│ Pretty Lowered: -//│ -//│ define fun Handler$h1$f$(x)(k) { -//│ return Handler$h1$f(x, k) -//│ } in -//│ define fun Handler$h1$f(x1, k1) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc = 0 in -//│ end -//│ else -//│ set pc = runtime.resumePc in -//│ set saveOffset = runtime.resumeIdx in -//│ set cur = runtime.resumeArr.saveOffset in -//│ set saveOffset = +(saveOffset, 1) in -//│ set result = runtime.resumeArr.saveOffset in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main = match pc -//│ 0 => -//│ match x1 -//│ true => -//│ return Predef.print("h1 stop") -//│ else -//│ set tmp = +(id, 1) in -//│ set id = tmp in -//│ set cur = id in -//│ set runtime.resumeValue = globalThis.String(cur) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$f, 7, "NestedHandlers.mls:21:29", null, null, 1, 2, x1, k1, 2, cur, result) -//│ in -//│ set pc = 7 in -//│ continue main -//│ in -//│ end -//│ 5 => -//│ set tmp1 = runtime.resumeValue in -//│ set runtime.resumeValue = k1(x1) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$f, 4, null, null, null, 1, 2, x1, k1, 2, cur, result) -//│ in -//│ set pc = 4 in -//│ continue main -//│ 1 => -//│ set tmp2 = runtime.resumeValue in -//│ return result -//│ 6 => -//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$f, 5, "NestedHandlers.mls:21:9", null, null, 1, 2, x1, k1, 2, cur, result) -//│ in -//│ set pc = 5 in -//│ continue main -//│ 2 => -//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$f, 1, "NestedHandlers.mls:23:9", null, null, 1, 2, x1, k1, 2, cur, result) -//│ in -//│ set pc = 1 in -//│ continue main -//│ 7 => -//│ set tmp3 = runtime.resumeValue in -//│ set tmp4 = +("h1 start ", tmp3) in -//│ set pc = 6 in -//│ continue main -//│ 3 => -//│ set tmp5 = runtime.resumeValue in -//│ set tmp6 = +("h1 end ", tmp5) in -//│ set pc = 2 in -//│ continue main -//│ 4 => -//│ set result = runtime.resumeValue in -//│ set runtime.resumeValue = globalThis.String(cur) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h1$f, 3, "NestedHandlers.mls:23:27", null, null, 1, 2, x1, k1, 2, cur, result) -//│ in -//│ set pc = 3 in -//│ continue main -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun Handler$h2$f$(x2)(k2) { -//│ return Handler$h2$f(x2, k2) -//│ } in -//│ define fun Handler$h2$f(x3, k3) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc1 = 0 in -//│ end -//│ else -//│ set pc1 = runtime.resumePc in -//│ set saveOffset1 = runtime.resumeIdx in -//│ set cur1 = runtime.resumeArr.saveOffset1 in -//│ set saveOffset1 = +(saveOffset1, 1) in -//│ set result1 = runtime.resumeArr.saveOffset1 in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main1 = match pc1 -//│ 0 => -//│ match x3 -//│ true => -//│ return Predef.print("h2 stop") -//│ else -//│ set tmp7 = +(id, 1) in -//│ set id = tmp7 in -//│ set cur1 = id in -//│ set runtime.resumeValue = globalThis.String(cur1) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$f, 7, "NestedHandlers.mls:32:29", null, null, 1, 2, x3, k3, 2, cur1, result1) -//│ in -//│ set pc1 = 7 in -//│ continue main1 -//│ in -//│ end -//│ 5 => -//│ set tmp8 = runtime.resumeValue in -//│ set runtime.resumeValue = k3(x3) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$f, 4, null, null, null, 1, 2, x3, k3, 2, cur1, result1) -//│ in -//│ set pc1 = 4 in -//│ continue main1 -//│ 1 => -//│ set tmp9 = runtime.resumeValue in -//│ return result1 -//│ 6 => -//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$f, 5, "NestedHandlers.mls:32:9", null, null, 1, 2, x3, k3, 2, cur1, result1) -//│ in -//│ set pc1 = 5 in -//│ continue main1 -//│ 2 => -//│ set runtime.resumeValue = Predef.print(‹not in scope: $tmp›) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$f, 1, "NestedHandlers.mls:34:9", null, null, 1, 2, x3, k3, 2, cur1, result1) -//│ in -//│ set pc1 = 1 in -//│ continue main1 -//│ 7 => -//│ set tmp10 = runtime.resumeValue in -//│ set tmp11 = +("h2 start ", tmp10) in -//│ set pc1 = 6 in -//│ continue main1 -//│ 3 => -//│ set tmp12 = runtime.resumeValue in -//│ set tmp13 = +("h2 end ", tmp12) in -//│ set pc1 = 2 in -//│ continue main1 -//│ 4 => -//│ set result1 = runtime.resumeValue in -//│ set runtime.resumeValue = globalThis.String(cur1) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(Handler$h2$f, 3, "NestedHandlers.mls:34:27", null, null, 1, 2, x3, k3, 2, cur1, result1) -//│ in -//│ set pc1 = 3 in -//│ continue main1 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun handleBlock$$(g, h1, h2)() { -//│ return handleBlock$(g, h1, h2) -//│ } in -//│ define class Handler$h2$ { -//│ let Handler$h2$$cap = ... -//│ preCtor { -//│ set tmp14 = super() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp14 = runtime.illegalEffect("in a constructor") in -//│ end -//│ in -//│ return tmp14 -//│ } -//│ fun class:Handler$h2$::f(x4) { -//│ set Handler$h2$f$here = Handler$h2$f$(x4) in -//│ return runtime.mkEffect(Handler$h2$, Handler$h2$f$here) -//│ } -//│ } in -//│ define fun handleBlock$(g1, h11, h21) { -//│ return g1(h11, h21) -//│ } in -//│ define fun handleBlock$$(g2, h12)() { -//│ return handleBlock$(g2, h12) -//│ } in -//│ define class Handler$h1$ { -//│ let Handler$h1$$cap = ... -//│ preCtor { -//│ set tmp15 = super() in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ set tmp15 = runtime.illegalEffect("in a constructor") in -//│ end -//│ in -//│ return tmp15 -//│ } -//│ fun class:Handler$h1$::f(x5) { -//│ set Handler$h1$f$here = Handler$h1$f$(x5) in -//│ return runtime.mkEffect(Handler$h1$, Handler$h1$f$here) -//│ } -//│ } in -//│ define fun handleBlock$(g3, h13) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc2 = 0 in -//│ end -//│ else -//│ set pc2 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main2 = match pc2 -//│ 0 => -//│ set h22 = new mut Handler$h2$() in -//│ set handleBlock$$here = handleBlock$$(g3, h13, h22) in -//│ set runtime.resumeValue = runtime.enterHandleBlock(h22, handleBlock$$here) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(handleBlock$, 1, null, null, null, 1, 2, g3, h13, 0) -//│ in -//│ set pc2 = 1 in -//│ continue main2 -//│ 1 => -//│ set tmp16 = runtime.resumeValue in -//│ return tmp16 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ define fun handleEffects(g4) { -//│ match runtime.resumePc -//│ -1 => -//│ set pc3 = 0 in -//│ end -//│ else -//│ set pc3 = runtime.resumePc in -//│ set runtime.resumePc = -1 in -//│ end -//│ in -//│ labelled loop main3 = match pc3 -//│ 0 => -//│ set h14 = new mut Handler$h1$() in -//│ set handleBlock$$here1 = handleBlock$$(g4, h14) in -//│ set runtime.resumeValue = runtime.enterHandleBlock(h14, handleBlock$$here1) in -//│ match runtime.curEffect -//│ null => -//│ end -//│ else -//│ return runtime.unwind(handleEffects, 1, null, null, null, 1, 1, g4, 0) -//│ in -//│ set pc3 = 1 in -//│ continue main3 -//│ 1 => -//│ set tmp17 = runtime.resumeValue in -//│ return tmp17 -//│ else -//│ -//│ in -//│ end in -//│ end -//│ } in -//│ set block$res3 = undefined in -//│ end fun f(h1, h2) = From 5f55896073c1ee363842c5f181a86a34232b00db Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 19 Jan 2026 17:11:12 +0800 Subject: [PATCH 16/18] add test --- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 13 ++------ .../src/test/mlscript/backlog/Lifter.mls | 33 +++++++++++++++---- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 64513bf5ce..464bd0f1ff 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,23 +9,13 @@ :todo :noSanityCheck - -fun test() = - class A with - fun get = 2 - class B() extends A - B().get -test() -//│ Elab: { fun member:test‹873›() = { Cls A { method fun member:get‹870› = 2; }; Cls BParamList(‹›,List(),None) { }; member:B‹872›#666().get }; member:test‹873›#666() } -//│ = 2 - :lift :sjs fun f(x) = fun g() = x fun h(a) = a() h(g) -//│ Elab: { fun member:f‹893›(x‹894›) = { fun member:g‹891›() = x‹894›#666; fun member:h‹892›(a‹899›) = a‹899›#666(); member:h‹892›#666(member:g‹891›#666) }; } +//│ Elab: { fun member:f‹688›(x‹689›) = { fun member:g‹686›() = x‹689›#666; fun member:h‹687›(a‹694›) = a‹694›#666(); member:h‹687›#666(member:g‹686›#666) }; } //│ JS (unsanitized): //│ let g, h, f, g$; //│ g$ = function g$(x) { @@ -40,3 +30,4 @@ fun f(x) = //│ return runtime.safeCall(a()) //│ }; //│ f = function f(x) { let g$here; g$here = g$(x); return h(g) }; + diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index e9be9caecb..a2684c5320 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -51,6 +51,25 @@ test(2) //│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +// This is due to `super` not being called with multiple parameter lists. +:expect "B()" +fun test(x) = + class A() with + fun get = x + class B() extends A() with + fun bar = x + B() +test(0).toString() +//│ ═══[RUNTIME ERROR] Expected: '"B()"', got: '"(x) => { this.x = x; return this; }"' +//│ = "(x) => { this.x = x; return this; }" + +// In fact, the correct way (using the current "constructor returns a lambda" hack) is this: +// return (params) => { +// super(parentMainParams); +// // ctor +// return this(parentAuxParams); +// } + /// The following are related to first-class classes, instance checks, and private fields. /// :w @@ -121,7 +140,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.117: module M with +//│ ║ l.136: module M with //│ ╙── ^ :expect 14 @@ -136,7 +155,7 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.132: module M with +//│ ║ l.151: module M with //│ ╙── ^ :expect 12 @@ -149,13 +168,13 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.148: module M with +//│ ║ l.167: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' //│ ╟── which references the symbol introduced here -//│ ║ l.148: module M with +//│ ║ l.167: module M with //│ ║ ^^^^^^ -//│ ║ l.149: fun getB() = x +//│ ║ l.168: fun getB() = x //│ ╙── ^^^^^^^^^^^^^^^^^^ :expect 2 @@ -172,7 +191,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.171: class Bar extends Foo +//│ ║ l.190: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. @@ -189,7 +208,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.186: module M with +//│ ║ l.205: module M with //│ ╙── ^ From 95603236a3f288a764fef02b4ff7c7a09774efc1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 19 Jan 2026 23:42:46 +0800 Subject: [PATCH 17/18] support objects, most tests working --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 185 ++++++++++------- .../main/scala/hkmc2/codegen/Lowering.scala | 2 +- .../main/scala/hkmc2/codegen/ScopeData.scala | 121 +++++++---- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 59 +++--- hkmc2/shared/src/test/mlscript/HkScratch.mls | 26 +++ hkmc2/shared/src/test/mlscript/HkScratch2.mls | 99 +++++++-- .../src/test/mlscript/backlog/Lifter.mls | 17 +- .../mlscript/handlers/NonLocalReturns.mls | 183 +++-------------- .../mlscript/handlers/ReturnInHandler.mls | 80 +------- .../test/mlscript/lifter/ModulesObjects.mls | 191 +++++------------- .../src/test/mlscript/tailrec/TailRecOpt.mls | 2 +- 11 files changed, 425 insertions(+), 540 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 694ca624e9..190805549c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -112,9 +112,11 @@ object Lifter: * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. */ -class Lifter(topLevelBlk: Block)(using State, Raise, Config): +class Lifter(topLevelBlk: Block, handlerPaths: HandlerPaths)(using State, Raise, Config): import Lifter.* + val handlerSyms: Set[Symbol] = Set(State.nonLocalRet, State.effectSigSymbol) + extension (l: Local) def asLocalPath: LocalPath = LocalPath.Sym(l) def asDefnRef: DefnRef = DefnRef.Sym(l) @@ -200,7 +202,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls: (ClassSymbol | ModuleOrObjectSymbol), _) => - if nestedScopes.contains(cls) && !ignored.contains(cls) && !data.getNode(cls).isInTopLevelMod then // don't generate a warning if it's already ignored + if nestedScopes.contains(cls) && !ignored.contains(cls) && !data.getNode(cls).isModOrTopLevel then // don't generate a warning if it's already ignored raise(WarningReport( msg"Cannot yet lift class/module `${cls.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation @@ -232,6 +234,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // If B extends A, then A -> B is an edge parentPath match case None => () + // for now, allow selecting runtime symbols + case Some(Select(qual = Value.Ref(l, _))) if State.runtimeSymbol is l => () + case Some(RefOfBms(_, S(s: ClassSymbol))) if handlerSyms.contains(s) => () + case Some(RefOfBms(s, _)) if handlerSyms.contains(s) => () case Some(RefOfBms(_, S(s: ClassSymbol))) => if nestedScopes.contains(s) then extendsGraph += (s -> isym) case _ if !ignored.contains(isym) => @@ -257,8 +263,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): override def applyValue(v: Value): Unit = v match case RefOfBms(_, S(l)) if nestedScopes.contains(l) => data.getNode(l).obj match + case c: ScopedObject.Class if c.isObj => () case c: (ScopedObject.Class | ScopedObject.ClassCtor) => - if !c.node.get.isInTopLevelMod then + if !c.node.get.isModOrTopLevel then raise(WarningReport( msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation @@ -314,6 +321,9 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // instantiations are rewritten. // // Does *not* rewrite references to non-lifted BMS symbols. + // + // References to methods and unlifted classes nested inside classes/modules are + // always rewritten using `this.defnName` (when accessed internally) or `object.defnName`. def rewriteBms(b: Block) = // BMS's that need to be created val syms: LinkedHashMap[FunSyms[?], Local] = LinkedHashMap.empty @@ -325,42 +335,38 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): def resolveDefnRef(l: BlockMemberSymbol, d: DefinitionSymbol[?], r: RewrittenScope[?]) = ctx.defnsMap.get(d) match case Some(defnRef) => S(defnRef.read) - case None => - ctx.defnsMap.get(d) match - case Some(value) => S(value.read) - case None => r.obj match - case r: ScopedObject.Referencable[?] => - // rewrite the parent - r.owner.flatMap(ctx.symbolsMap.get(_)) match - case Some(value) => - S(Select(value.read, Tree.Ident(l.nme))(S(d))) - case None => N - case _ => N + case None => r.obj match + case c: ScopedObject.Class if c.isObj && r.isInstanceOf[LiftedClass] => + S(ctx.symbolsMap(c.cls.isym).read) + case r: ScopedObject.Referencable[?] => + // rewrite the parent + r.owner.flatMap(ctx.symbolsMap.get(_)) match + case Some(value) => + S(Select(value.read, Tree.Ident(l.nme))(S(d))) + case None => N + case _ => N - override def applyResult(r: Result)(k: Result => Block): Block = r match + override def applyResult(r: Result)(k: Result => Block): Block = + r match // if possible, directly rewrite the call using the efficient version case c @ Call(RefOfBms(l, S(d)), args) => - applyArgs(args): newArgs => - def join1: Call = - if args is newArgs then c - else c.copy(args = newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) - val newRes: Call = ctx.rewrittenScopes.get(d) match - case N => join1 - case S(r) => - def join2: Call = + ctx.rewrittenScopes.get(d) match + case N => super.applyResult(r)(k) // external call, or have not yet traversed that function + case S(r) => + applyArgs(args): newArgs => + def join2: Block = resolveDefnRef(l, d, r) match - case Some(value) => c.copy(fun = value, args = newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) - case None => join1 + case Some(value) => k(c.copy(fun = value, args = newArgs)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall)) + case None => super.applyResult(c)(k) r match - // function call - case f: LiftedFunc => f.rewriteCall(c, newArgs) - // ctor call (without using `new`) - case ctor: RewrittenClassCtor => ctor.getRewrittenCls match - case cls: LiftedClass => - cls.rewriteCall(c, newArgs) + // function call + case f: LiftedFunc => k(f.rewriteCall(c, newArgs)) + // ctor call (without using `new`) + case ctor: RewrittenClassCtor => ctor.getRewrittenCls match + case cls: LiftedClass => + k(cls.rewriteCall(c, newArgs)) + case _ => join2 case _ => join2 - case _ => join2 - k(newRes) case inst @ Instantiate(mut, RefOfBms(l, S(d)), args) => applyArgs(args): newArgs => def join = @@ -465,25 +471,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): path.assign(rhs2, applySubBlock(rest)) case _ => super.applyBlock(rewritten) - // rewrite object definitions, assigning to the given symbol in modObjLocals - case Define(d: ClsLikeDefn, rest: Block) => super.applyBlock(rewritten) // TODO - /* - ctx.modObjLocals.get(d.sym) match - case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match - case Some(_) => // has args - extraLocals.add(sym) - blockBuilder - .assign(sym, Instantiate(mut = false, d.sym.asPath, getCallArgs(FunSyms(d.sym, d.isym), ctx))) - .rest(applyBlock(rest)) - case None => // has no args - // Objects with no parameters are instantiated statically - blockBuilder - .assign(sym, d.sym.asPath) - .rest(applyBlock(rest)) - case _ => ctx.replacedDefns.get(d.sym) match - case Some(value) => Define(value, applyBlock(rest)) - case None => super.applyBlock(rewritten) - */ + // rewrite object definitions, assigning to the saved symbol + case Define(d @ ClsLikeDefn(k = syntax.Obj), rest: Block) => ctx.liftedScopes.get(d.isym) match + case Some(l: LiftedClass) if l.obj.isObj => + ctx.symbolsMap(l.cls.isym).assign(l.instObject, applySubBlock(rest)) + case _ => super.applyBlock(rewritten) case _ => super.applyBlock(rewritten) pre.rest(remaining) @@ -654,12 +646,13 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): lazy val captureClass = captureInfo._1 lazy val captureMap = captureInfo._2.toMap + lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] lazy val capturePath: Path protected def rewriteImpl: LifterResult[T] - protected final def addCaptureSym(b: Block, captureSym: Local, define: Bool): Block = + protected final def addExtraSyms(b: Block, captureSym: Local, objSyms: Iterable[Local], define: Bool): Block = if hasCapture then val undef = Value.Lit(Tree.UnitLit(false)).asArg val inst = Instantiate( @@ -671,12 +664,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val assign = Assign(captureSym, inst, b) if define then Scoped( - Set(captureSym), + Set(captureSym) ++ objSyms, assign ) else assign else - b + if define then Scoped(objSyms.toSet, b) else b /** * Rewrites the contents of this scoped object to reference the lifted versions of variables. @@ -712,6 +705,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case ScopeNode(obj = c: ScopedObject.Companion) => val s: Local = c.comp.isym s -> LocalPath.BmsRef(c.bsym, c.comp.isym) + case ScopeNode(obj = c: ScopedObject.Class) if c.isObj => + c.cls.isym -> (liftedObjsMap.get(c.cls.isym) match + case Some(value) => value // lifted + case None => LocalPath.BmsRef(c.bsym, c.cls.isym) // not lifted + ) + .toMap // Note: the order here is important, as fromCap must override keys from // fromThisObj. @@ -748,7 +747,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): * as a parameter. Does not include neighbouring objects that this definition may lose * access to. Those are in a separate list. * - * Includes inner symbols introduced by modules. + * Includes symbols introduced by modules and objects. */ final val reqSymbols = accessed ++ allRefdScopes.map(data.getNode(_).obj) .collect: @@ -758,6 +757,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): .filter: d => data.getNode(d).obj match case _: ScopedObject.Companion => true + case c: ScopedObject.Class if c.isObj => true case _ => false private val (reqPassedSymbols, captures) = reqSymbols @@ -778,7 +778,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): * Neighbouring objects that this definition may lose access to * once lifted, referenced by their *definition symbol* (not BMS). */ - final val reqDefns = node.reqCaptureObjs.map(_.sym).toSet.intersect(refdDSyms) + final val reqDefns = node.reqCaptureObjs + .filter: + case f: ScopedObject.Func if f.isMethod.isDefined => false + case _ => true + .map(_.sym) + .toSet.intersect(refdDSyms) /** Maps directly passed locals to the path representing that local within this object. */ protected val passedSymsMap: Map[Local, LocalPath] @@ -838,8 +843,25 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): sealed trait GenericRewrittenScope[T] extends RewrittenScope[T]: lazy val captureSym = VarSymbol(Tree.Ident(obj.nme + "$cap")) override lazy val capturePath = captureSym.asPath + protected val liftedObjsSyms: Map[InnerSymbol, VarSymbol] = node.liftedObjSyms.map: s => + s -> VarSymbol(Tree.Ident(s.nme + "$")) + .toMap + override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = liftedObjsSyms.map: + case k -> v => k -> v.asLocalPath + + protected def addExtraSyms(b: Block): Block = addExtraSyms(b, captureSym, liftedObjsSyms.values, true) - protected def addCaptureSym(b: Block): Block = addCaptureSym(b, captureSym, true) + /** + * A rewritten scope with a TermSymbol capture symbol. + */ + sealed trait ClsLikeRewrittenScope[T](sym: InnerSymbol) extends RewrittenScope[T]: + lazy val captureSym = TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(obj.nme + "$cap")) + override lazy val capturePath = captureSym.asPath + protected val liftedObjsSyms: Map[InnerSymbol, TermSymbol] = node.liftedObjSyms.map: s => + s -> TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(s.nme + "$")) + .toMap + override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = liftedObjsSyms.map: + case k -> v => k -> v.asLocalPath // some helpers private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme))) @@ -858,7 +880,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case s @ ScopeNode(obj = l: ScopedObject.Liftable[?]) if s.isLifted => l.defn.sym val (syms, rewritten) = (obj.block.syms.toSet -- liftedChildSyms, rewriter.rewrite(obj.block.body)) - val withCapture = addCaptureSym(rewritten) + val withCapture = addExtraSyms(rewritten) LifterResult(Scoped(syms, withCapture), rewriter.extraDefns.toList) class RewrittenLoop(override val obj: ScopedObject.Loop)(using ctx: LifterCtxNew) extends RewrittenScope[Block](obj) with GenericRewrittenScope[Block]: @@ -866,7 +888,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val rewriter = new BlockRewriter val rewritten = rewriter.rewrite(obj.body) - val withCapture = addCaptureSym(rewritten) + val withCapture = addExtraSyms(rewritten) LifterResult(withCapture, rewriter.extraDefns.toList) class RewrittenFunc(override val obj: ScopedObject.Func)(using ctx: LifterCtxNew) extends RewrittenScope[FunDefn](obj) with GenericRewrittenScope[FunDefn]: @@ -874,7 +896,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val rewriter = new BlockRewriter val rewritten = rewriter.rewrite(obj.fun.body) - val withCapture = addCaptureSym(rewritten) + val withCapture = addExtraSyms(rewritten) LifterResult(obj.fun.copy(body = withCapture)(obj.fun.forceTailRec), rewriter.extraDefns.toList) private def rewriteMethods(node: ScopeNode, methods: List[FunDefn])(using ctx: LifterCtxNew) = @@ -887,15 +909,16 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): LifterResult(liftedMtds, extras.flatten) class RewrittenClassCtor(override val obj: ScopedObject.ClassCtor)(using ctx: LifterCtxNew) extends RewrittenScope[Unit](obj): - override lazy val capturePath: Path = lastWords("tried to create a capture class for a class ctor") + override lazy val liftedObjsMap: Map[InnerSymbol, LocalPath] = lastWords("tried to create obj syms for a class ctor") override protected def rewriteImpl: LifterResult[Unit] = LifterResult((), Nil) // dummy def getRewrittenCls = ctx.rewrittenScopes(obj.cls.isym) class RewrittenClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) - extends RewrittenScope[ClsLikeDefn](obj): + extends RewrittenScope[ClsLikeDefn](obj) + with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -905,19 +928,20 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val rewriterPreCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) - val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) + val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( ctor = ctorWithCap, preCtor = rewrittenPrector, - privateFields = captureSym :: obj.cls.privateFields, + privateFields = captureSym :: liftedObjsSyms.values.toList ::: obj.cls.privateFields, methods = newMtds, ) LifterResult(newCls, rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras) class RewrittenCompanion(override val obj: ScopedObject.Companion)(using ctx: LifterCtxNew) - extends RewrittenScope[ClsLikeBody](obj): + extends RewrittenScope[ClsLikeBody](obj) + with ClsLikeRewrittenScope[ClsLikeBody](obj.comp.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.comp.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -925,11 +949,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): override def rewriteImpl: LifterResult[ClsLikeBody] = val rewriterCtor = new BlockRewriter val rewrittenCtor = rewriterCtor.rewrite(obj.comp.ctor) - val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) + val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) val LifterResult(newMtds, extras) = rewriteMethods(node, obj.comp.methods) val newComp = obj.comp.copy( ctor = ctorWithCap, - privateFields = captureSym :: obj.comp.privateFields, + privateFields = captureSym :: liftedObjsSyms.values.toList ::: obj.comp.privateFields, methods = newMtds ) LifterResult(newComp, rewriterCtor.extraDefns.toList ::: extras) @@ -976,7 +1000,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case Nil => PlainParamList(auxParams) :: Nil val rewriter = new BlockRewriter val newBod = rewriter.rewrite(fun.body) - val withCapture = addCaptureSym(newBod) + val withCapture = addExtraSyms(newBod) val newDefn = fun.copy(owner = N, sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)(fun.forceTailRec) LifterResult(newDefn, rewriter.extraDefns.toList) @@ -1042,7 +1066,8 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): if isTrivial then LifterResult(lifted, extra) else LifterResult(lifted, mkAuxDefn :: extra) class LiftedClass(override val obj: ScopedObject.Class)(using ctx: LifterCtxNew) - extends LiftedScope[ClsLikeDefn](obj): + extends LiftedScope[ClsLikeDefn](obj) + with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap")) override lazy val capturePath: Path = captureSym.asPath @@ -1089,7 +1114,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val flattenedDSym = TermSymbol.fromFunBms(flattenedSym, N) def mkFlattenedDefn: Opt[FunDefn] = - if isTrivial then return N + if isTrivial || obj.isObj then return N val auxSyms = auxParams.map(p => VarSymbol(Tree.Ident(p.sym.nme))) val main = obj.cls.paramsOpt match case Some(value) => dupParamList(value) @@ -1132,9 +1157,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): )) S(FunDefn(N, flattenedSym, flattenedDSym, params :: Nil, bod)(false)) - + + def instObject = Instantiate(true, Value.Ref(cls.sym, S(cls.isym)), formatArgs) def rewriteInstantiate(inst: Instantiate, args: List[Arg]): Result = + if obj.isObj then lastWords("tried to rewrite instantiate for an object") if isTrivial then if inst.args is args then inst else inst.copy(args = args) @@ -1145,6 +1172,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): )(true, config.checkInstantiateEffect, false) def rewriteCall(c: Call, args: List[Arg])(using ctx: LifterCtxNew): Call = + if obj.isObj then lastWords("tried to rewrite instantiate for an object") if isTrivial then if c.args is args then c else c.copy(args = args)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) @@ -1164,7 +1192,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val rewrittenCtor = rewriterCtor.rewrite(obj.cls.ctor) val rewrittenPrector = rewriterPreCtor.rewrite(obj.cls.preCtor) - val ctorWithCap = addCaptureSym(rewrittenCtor, captureSym, false) + val ctorWithCap = addExtraSyms(rewrittenCtor, captureSym, Nil, false) // Assign passed locals and captures val ctorWithPassed = passedSymsOrdered.foldRight(ctorWithCap): @@ -1175,6 +1203,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case (sym, acc) => val (vs, ts) = capSymsMap_(sym) Assign(ts, vs.asPath, acc) + val ctorWithDefns = passedDefnsOrdered.foldRight(ctorWithCaps): + case (sym, acc) => + val (vs, ts) = defnSymsMap_(sym) + Assign(ts, vs.asPath, acc) val newAuxList = if isTrivial then cls.auxParams @@ -1183,9 +1215,10 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val LifterResult(newMtds, extras) = rewriteMethods(node, obj.cls.methods) val newCls = obj.cls.copy( owner = N, - ctor = ctorWithCaps, + k = syntax.Cls, // turn objects into classes + ctor = ctorWithDefns, preCtor = rewrittenPrector, - privateFields = captureSym :: obj.cls.privateFields, + privateFields = captureSym :: liftedObjsSyms.values.toList ::: obj.cls.privateFields, methods = newMtds, auxParams = newAuxList ) @@ -1229,7 +1262,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): ctx.symbolsMap ++= scope.symbolsMap ctx.capturesMap ++= scope.capturePaths ctx.defnsMap ++= scope.defnPaths - + val rewrittenScopes = node.children.map(createRewritten) // The scopes in `lifted` will be rewritten right now // The scopes in `ignored` will be rewritten in-place when traversing the block diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 66d3fb4512..16da7399b8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -1053,7 +1053,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): else withHandlers1 val lifted = - if lift then Lifter(scopeFlattened).transform + if lift then Lifter(scopeFlattened, handlerPaths).transform else scopeFlattened val (withHandlers2, stackSafetyInfo) = config.effectHandlers.fold((lifted, Map.empty)): opt => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala index f312179d45..398ae8004f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ScopeData.scala @@ -43,7 +43,7 @@ object ScopeData: var node: Opt[TScopeNode[T]] = N lazy val toInfo: ScopedInfo = this match case Top(_) => () - case Class(cls) => cls.isym + case Class(cls, _) => cls.isym case Companion(comp, par) => comp.isym case ClassCtor(cls) => cls.ctorSym.get case Func(fun, _) => fun.dSym @@ -53,7 +53,7 @@ object ScopeData: // note: not unique lazy val nme = this match case Top(b) => "top" - case Class(cls) => cls.isym.nme + case Class(cls, _) => cls.isym.nme case Companion(comp, par) => comp.isym.nme + "_mod" case ClassCtor(cls) => cls.isym.nme // should be unused case Func(fun, isMethod) => fun.dSym.nme @@ -65,7 +65,7 @@ object ScopeData: // we want definedLocals for the top level scope to be empty, because otherwise, // the lifter may try to capture those locals. case Top(b) => Set.empty - case Class(cls) => + case Class(cls, _) => // Public fields are not included, as they are accessed using // a field selection rather than directly using the BlockMemberSymbol. val paramsSet: Set[Local] = cls.paramsOpt match @@ -86,7 +86,7 @@ object ScopeData: def contents: T = this match case Top(b) => b - case Class(cls) => cls + case Class(cls, _) => cls case Companion(comp, par) => comp case ClassCtor(cls) => () case Func(fun, _) => fun @@ -96,17 +96,17 @@ object ScopeData: // Scoped nodes which may be referenced using a symbol. sealed abstract class Referencable[T] extends TScopedObject[T]: def sym: LiftedSym = this match - case Class(cls) => cls.isym + case Class(cls, _) => cls.isym case Companion(comp, par) => comp.isym case Func(fun, isMethod) => fun.dSym case ClassCtor(cls) => cls.ctorSym.get def bsym: BlockMemberSymbol = this match - case Class(cls) => cls.sym + case Class(cls, _) => cls.sym case Companion(comp, par) => par.sym case Func(fun, isMethod) => fun.sym case ClassCtor(cls) => cls.sym def owner: Opt[InnerSymbol] = this match - case Class(cls) => cls.owner + case Class(cls, _) => cls.owner case Companion(comp, par) => par.owner case ClassCtor(cls) => cls.owner case Func(fun, isMethod) => fun.owner @@ -118,7 +118,7 @@ object ScopeData: // The top-level scope. case class Top(b: Block) extends ScopedObject[Block] // b may be a scoped block, in which case, its variables represent the top-level variables. - case class Class(cls: ClsLikeDefn) extends Liftable[ClsLikeDefn]: + case class Class(cls: ClsLikeDefn, isObj: Bool) extends Liftable[ClsLikeDefn]: val defn = cls case class Companion(comp: ClsLikeBody, par: ClsLikeDefn) extends Referencable[ClsLikeBody] // We model it like this: the ctor is just another function in the same scope as the class and initializes the corresponding class @@ -141,7 +141,7 @@ object ScopeData: obj match case ScopedObject.Top(b) => traverser.applyBlock(b) case ScopedObject.Class(ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, methods, - privateFields, publicFields, preCtor, ctor, mod, bufferable)) + privateFields, publicFields, preCtor, ctor, mod, bufferable), _) => // do not traverse the companion -- it is a separate kind of scoped object // and will therefore be traversed separately @@ -187,19 +187,16 @@ object ScopeData: // does not include variables introduced by itself lazy val existingVars: Set[Local] = parent match - case Some(value) => value.existingVars ++ value.obj.definedLocals + case Some(value) => value.existingVars ++ value.obj.definedLocals ++ value.nestedObjSyms case None => Set.empty lazy val isTopLevel: Bool = parent match case Some(ScopeNode(obj = _: ScopedObject.Top)) => true case _ => false - lazy val isInTopLevelMod: Bool = parent match + lazy val isModOrTopLevel: Bool = parent match case Some(par) => par.obj match - case _: ScopedObject.Companion => par.isInTopLevelMod - case s: ScopedObject.ScopedBlock => s.node.get.parent.get.obj match - case c: ScopedObject.Companion => c.node.get.parent.get.isInTopLevelMod - case _ => false + case _: ScopedObject.Companion => true case _ => false case None => true @@ -210,7 +207,15 @@ object ScopeData: case c @ ScopeNode(obj = s: ScopedObject.Referencable[?]) => s.bsym obj.definedLocals -- rmv case _ => obj.definedLocals - + + lazy val nestedObjSyms: Set[InnerSymbol] = children.collect: + case ScopeNode(obj = c: ScopedObject.Class) if c.isObj => c.cls.isym + .toSet + + lazy val liftedObjSyms: Set[InnerSymbol] = children.collect: + case n @ ScopeNode(obj = c: ScopedObject.Class) if c.isObj && n.isLifted => c.cls.isym + .toSet + lazy val inScopeISyms: Set[Local] = val parVals = parent match case Some(value) => value.inScopeISyms @@ -224,22 +229,31 @@ object ScopeData: // All of the following must not be called until ignoredScopes is populated with the relevant data. lazy val isLifted: Bool = - val ignored = ignoredScopes.ignored match - case Some(value) => value - case None => lastWords("isLifted accessed before the set of ignored scopes was set") - - parent.map(_.obj) match - case Some(_: ScopedObject.Companion) => false // there is no need to lift objects nested inside a module - case _ => + def impl: Bool = + val ignored = ignoredScopes.ignored match + case Some(value) => value + case None => lastWords("isLifted accessed before the set of ignored scopes was set") + + // check if the owner is a module obj match - // case _: ScopedObject.Companion => false - // case c: ScopedObject.Class if c.cls.companion.isDefined => false - case _ if ignored.contains(obj.toInfo) => false - case _ if isInTopLevelMod => false - case ScopedObject.Func(isMethod = S(true)) => false - case _: ScopedObject.Loop | _: ScopedObject.ClassCtor | _: ScopedObject.ScopedBlock | _: ScopedObject.Companion => false - case _ => true - + case r: ScopedObject.Referencable[?] => r.owner match + case Some(value) => if value.asMod.isDefined then return false + case _ => () + case _ => () + + parent.map(_.obj) match + case Some(_: ScopedObject.Companion) => false // there is no need to lift objects nested inside a module + case _ => + obj match + // case _: ScopedObject.Companion => false + // case c: ScopedObject.Class if c.cls.companion.isDefined => false + case _ if ignored.contains(obj.toInfo) => false + case _ if isModOrTopLevel => false + case ScopedObject.Func(isMethod = S(true)) => false + case _: ScopedObject.Loop | _: ScopedObject.ClassCtor | _: ScopedObject.ScopedBlock | _: ScopedObject.Companion => false + case _ => true + impl + lazy val liftedChildNodes: List[ScopeNode[?]] = if isLifted then this :: Nil else children.flatMap(_.liftedChildNodes) @@ -267,6 +281,7 @@ object ScopeData: case _ => if isLifted then reqCaptureObjsImpl else parent.get.reqCaptureObjsImpl + def dSymUnapply(data: ScopeData, v: DefinitionSymbol[?] | Option[DefinitionSymbol[?]]) = v match case Some(d) if data.contains(d) => S(d) @@ -313,7 +328,7 @@ class ScopeData(b: Block)(using State, IgnoredScopes): override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => applyFunDefn(f) case c: ClsLikeDefn => - objs ::= ScopedObject.Class(c) + objs ::= ScopedObject.Class(c, c.k === syntax.Obj) c.ctorSym match case Some(value) => objs ::= ScopedObject.ClassCtor(c) case None => () @@ -326,15 +341,31 @@ class ScopeData(b: Block)(using State, IgnoredScopes): def scopeFinder = new ScopeFinder() def makeScopeTreeRec[T](obj: TScopedObject[T]): TScopeNode[T] = + // An annoying thing with class ctors: + // Sometimes, nested classes/objects appear inside the top-level scoped block of class/module ctor, + // despite being a child of the class, but we want these to be a direct child of the class/module. val finder = scopeFinder + val ctorFinder = scopeFinder + val ctorScoped = obj match + case ScopedObject.Class(cls, _) => cls.ctor match + case s: Scoped => S((s, cls.isym)) + case _ => N + case ScopedObject.Companion(comp, _) => comp.ctor match + case s: Scoped => S((s, comp.isym)) + case _ => N + case _ => N obj match case ScopedObject.Top(s: Scoped) => finder.applyBlock(s.body) case ScopedObject.Top(b) => finder.applyBlock(b) - case ScopedObject.Class(cls) => + case ScopedObject.Class(cls, _) => finder.applyBlock(cls.preCtor) - finder.applyBlock(cls.ctor) + ctorScoped match + case Some((value, _)) => ctorFinder.applyBlock(value.body) + case None => finder.applyBlock(cls.ctor) case ScopedObject.Companion(comp, par) => - finder.applyBlock(comp.ctor) + ctorScoped match + case Some((value, _)) => ctorFinder.applyBlock(value.body) + case None => finder.applyBlock(comp.ctor) case ScopedObject.Func(fun, _) => finder.applyBlock(fun.body) case ScopedObject.ScopedBlock(_, block) => @@ -342,10 +373,26 @@ class ScopeData(b: Block)(using State, IgnoredScopes): case ScopedObject.ClassCtor(c) => () case ScopedObject.Loop(_, b) => finder.applyBlock(b) val mtdObjs = obj match - case ScopedObject.Class(cls) => cls.methods.map(ScopedObject.Func(_, S(true))) + case ScopedObject.Class(cls, _) => cls.methods.map(ScopedObject.Func(_, S(true))) case ScopedObject.Companion(comp, par) => comp.methods.map(ScopedObject.Func(_, S(false))) case _ => Nil - val children = (mtdObjs ::: finder.objs).map(makeScopeTreeRec) + + // This extracts owned definitions from the ctor and makes them a descendant of the class scope node, + // while making the other scoped objects in the ctor a descendant of the ctor's scoped block node. + val (ctorNode, ctorObjs) = ctorScoped match + case Some((ctor, isym)) => + val ctorBlkObj = ScopedObject.ScopedBlock(fresh.make, ctor) + val (ctorObjs, ctorBlkChildren) = ctorFinder.objs.partitionMap: + case a: ScopedObject.Referencable[?] if a.owner.isDefined && a.owner.get === isym => L(a) + case a => R(a) + val children = ctorBlkChildren.map(makeScopeTreeRec) + val ctorNde = ScopeNode.ScopeNode(ctorBlkObj, N, children) + ctorBlkObj.node = S(ctorNde) + for c <- children do c.parent = S(ctorNde) + (S(ctorNde), ctorObjs) + case None => (N, Nil) + + val children = (ctorObjs ::: mtdObjs ::: finder.objs).map(makeScopeTreeRec).prependedAll(ctorNode) val retNode = ScopeNode.ScopeNode(obj, N, children) obj.node = S(retNode) for c <- children do c.parent = S(retNode) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 1dea65b89e..ded3a92436 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -65,6 +65,8 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case v: ValDefn => applyDefn(v) applySubBlock(d.rest) + case c @ ClsLikeDefn(k = syntax.Obj) => + accessed.refdDefns.add(c.isym) case _ => applySubBlock(d.rest) case _ => super.applyBlock(b) @@ -81,7 +83,8 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes // definitions that access a module's method directly need an edge to that method case ScopedObject.Func(isMethod = N | S(false)) => accessed.refdDefns.add(node.obj.toInfo) - case _: ScopedObject.Class | _: ScopedObject.ClassCtor => accessed.refdDefns.add(node.obj.toInfo) + case c: ScopedObject.Class if c.isObj => accessed.accessed.add(c.cls.isym) + case _: ScopedObject.Class | _: ScopedObject.ClassCtor | _: ScopedObject.Companion => accessed.refdDefns.add(node.obj.toInfo) case _ => () case Value.Ref(l, _) => @@ -104,7 +107,7 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes case _ => blkAccessesShallow(b) case ScopedObject.Func(f, _) => blkAccessesShallow(f.body) - case ScopedObject.Class(c) => + case ScopedObject.Class(c, _) => // We must assume that classes may access all their methods. // When the class symbol is referenced once, that symbol may be used in // arbitrary ways, which includes calling any of this class's methods. @@ -270,14 +273,14 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val accessMap = m2.foldLeft[Map[ScopedInfo, AccessInfo]](Map.empty)(_ ++ _) private def reqdCaptureLocals(s: ScopeNode): Map[ScopedInfo, Set[Local]] = - val (blk, extraMtds) = s.obj match + val blk = s.obj match case ScopedObject.Top(b) => lastWords("reqdCaptureLocals called on top block") case ScopedObject.ClassCtor(cls) => return Map.empty + (s.obj.toInfo -> Set.empty) - case ScopedObject.Class(cls) => (Begin(cls.preCtor, cls.ctor), cls.methods) - case ScopedObject.Companion(comp, _) => (comp.ctor, comp.methods) - case ScopedObject.Func(fun, _) => (fun.body, Nil) - case ScopedObject.ScopedBlock(uid, block) => (block, Nil) - case ScopedObject.Loop(sym, block) => (block, Nil) + case ScopedObject.Class(cls, _) => Begin(cls.preCtor, cls.ctor) + case ScopedObject.Companion(comp, _) => comp.ctor + case ScopedObject.Func(fun, _) => fun.body + case ScopedObject.ScopedBlock(uid, block) => block + case ScopedObject.Loop(sym, block) => block // traverse all scoped blocks and loops val nexts: Buffer[ScopeNode] = Buffer.empty @@ -292,13 +295,18 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes val cap = reqdCaptureLocalsBlk(blk, nexts.toList, s.obj.definedLocals, locals) - // Variables mutated by a lifted child of a class methods requires a capture - val additional = extraMtds - .flatMap: mtd => - scopeData.getNode(mtd).liftedChildNodes.map(x => x.obj.toInfo) - .foldLeft(AccessInfo.empty): - case (acc, value) => acc ++ accessMap(value) - val newCap = cap ++ additional.mutated + // In a class, all variables that are mutated by a child scope and accessed by a lifted class must be captured + val additional = s.obj match + case _: ScopedObject.Companion | _: ScopedObject.Class => + val (a, b) = s.children.map: c => + val acc = accessMap(c.obj.toInfo) + val accAll = accessMapWithIgnored(c.obj.toInfo) + (accAll.mutated, acc.accessed) + .unzip + a.flatten.toSet.intersect(b.flatten.toSet) + case _ => Set.empty + + val newCap = cap ++ additional val cur: Map[ScopedInfo, Set[Local]] = nodes.map: n => n.obj.toInfo -> newCap.intersect(n.obj.definedLocals) @@ -341,13 +349,15 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes rec(s.body)(using linearVars = linearVars ++ s.syms) |> merge case l: Label if l.loop => rec(l.body)(using linearVars = Set.empty) |> merge - applyBlock(l.rest) + applySubBlock(l.rest) case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs if !linearVars.contains(lhs) then mutated += lhs - applyBlock(rest) - + applySubBlock(rest) + case Define(c @ ClsLikeDefn(k = syntax.Obj), rest) => + handleCalledScope(c.isym) + applySubBlock(rest) case Match(scrut, arms, dflt, rest) => applyPath(scrut) val infos = arms.map: @@ -357,15 +367,15 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes infos.foreach(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive dfltInfo.foreach(merge) - applyBlock(rest) + applySubBlock(rest) case Begin(sub, rest) => rec(sub) |> merge - applyBlock(rest) + applySubBlock(rest) case TryBlock(sub, finallyDo, rest) => // sub and finallyDo could be executed sequentially, so we must merge rec(sub) |> merge rec(finallyDo) |> merge - applyBlock(rest) + applySubBlock(rest) case Return(res, false) => applyResult(res) hasReader = Set.empty @@ -429,8 +439,11 @@ class UsedVarAnalyzer(b: Block, scopeData: ScopeData)(using State, IgnoredScopes scopeInfos.get(d) match case None => super.applyPath(p) case Some(defn) => - val isMod = defn.obj.isInstanceOf[ScopedObject.Companion] - if isMod then super.applyPath(p) + val isModOrObj = defn.obj match + case c: ScopedObject.Companion => true + case c: ScopedObject.Class => c.isObj + case _ => false + if isModOrObj then super.applyPath(p) else val AccessInfo(accessed, muted, refd) = accessMapWithIgnored(d) val muts = muted.intersect(thisVars) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index b09a983866..04f41a70de 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -11,3 +11,29 @@ class Eff //│ Elab: { Cls Eff { }; } +:lift +:sjs +fun f = + object A + fun foo = A +//│ Elab: { fun member:f‹693› = { Obj A { }; fun member:foo‹692› = member:A‹691›#666; () }; } +//│ JS (unsanitized): +//│ let A1, foo, f, foo$; +//│ foo$ = function foo$(A2) { +//│ return () => { +//│ return foo(A2) +//│ } +//│ }; +//│ foo = function foo(A2) { +//│ return A2 +//│ }; +//│ (class A { +//│ static { +//│ A1 = this +//│ } +//│ constructor() {} +//│ #A$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); +//│ f = function f() { let A$; A$ = new A1(); return runtime.Unit }; diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 464bd0f1ff..8873782395 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -9,25 +9,88 @@ :todo :noSanityCheck + :lift -:sjs -fun f(x) = - fun g() = x - fun h(a) = a() - h(g) -//│ Elab: { fun member:f‹688›(x‹689›) = { fun member:g‹686›() = x‹689›#666; fun member:h‹687›(a‹694›) = a‹694›#666(); member:h‹687›#666(member:g‹686›#666) }; } -//│ JS (unsanitized): -//│ let g, h, f, g$; -//│ g$ = function g$(x) { -//│ return () => { -//│ return g(x) +:ssjs +module M with + data class A(x) with + class B() with + fun newA_B(y) = + newA_B(2) + A(y) + fun newB = B() +M.A(2).newB +//│ Elab: { Mod M { Cls AParamList(‹›,List(Param(‹val›,x‹879›,None,Modulefulness(None))),None) { val member:x‹882› = x‹879›#0; Cls BParamList(‹›,List(),None) { method fun member:newA_B‹870›(y‹889›) = { (class:B‹885›#666.)newA_B‹member:newA_B‹870››(2); (module:M‹875›#666.)A‹member:A‹873››(y‹889›#666) }; }; method fun member:newB‹871› = (class:A‹877›#666.)B‹member:B‹872››(); }; }; member:M‹874›#666.A‹member:A‹873››(2).newB } +//│ JS: +//│ let B1, M1, tmp, B$; +//│ B$ = function B$(isMut, A$, A$1, B$1, M2) { +//│ let tmp1; +//│ if (isMut === true) { +//│ tmp1 = new B1.class(); +//│ } else { +//│ tmp1 = globalThis.Object.freeze(new B1.class()); //│ } +//│ return tmp1(A$, A$1, B$1, M2) //│ }; -//│ g = function g(x) { -//│ return x -//│ }; -//│ h = function h(a) { -//│ return runtime.safeCall(a()) +//│ B1 = function B() { +//│ return (A$, A$1, B$1, M2) => { +//│ return globalThis.Object.freeze(new B.class()(A$, A$1, B$1, M2)); +//│ } //│ }; -//│ f = function f(x) { let g$here; g$here = g$(x); return h(g) }; - +//│ (class B { +//│ static { +//│ B1.class = this +//│ } +//│ constructor() { +//│ return (A$, A$1, B$1, M2) => { +//│ this.A$ = A$; +//│ this.A$ = A$1; +//│ this.B$ = B$1; +//│ this.M = M2; +//│ return this; +//│ } +//│ } +//│ #B$cap; +//│ newA_B(y) { +//│ let tmp1; +//│ tmp1 = this.newA_B(2); +//│ return this.A$(y) +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "B", []]; +//│ }); +//│ (class M { +//│ static { +//│ M1 = this +//│ } +//│ constructor() { +//│ runtime.Unit; +//│ } +//│ static #M_mod$cap; +//│ static { +//│ this.A = function A(x) { +//│ return globalThis.Object.freeze(new A.class(x)); +//│ }; +//│ (class A { +//│ static { +//│ M.A.class = this +//│ } +//│ constructor(x) { +//│ this.x = x; +//│ } +//│ #A$cap; +//│ get newB() { +//│ return B$(false, M.A.class, M.A, this.B, M); +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A", ["x"]]; +//│ }); +//│ } +//│ #M$cap; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "M"]; +//│ }); +//│ tmp = M1.A(2); +//│ block$res1 = tmp.newB; +//│ undefined +//│ = B() diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index a2684c5320..35a0b3c4ff 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -140,7 +140,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.136: module M with +//│ ║ l.140: module M with //│ ╙── ^ :expect 14 @@ -155,7 +155,7 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.151: module M with +//│ ║ l.155: module M with //│ ╙── ^ :expect 12 @@ -168,13 +168,13 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.167: module M with +//│ ║ l.171: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' //│ ╟── which references the symbol introduced here -//│ ║ l.167: module M with +//│ ║ l.171: module M with //│ ║ ^^^^^^ -//│ ║ l.168: fun getB() = x +//│ ║ l.172: fun getB() = x //│ ╙── ^^^^^^^^^^^^^^^^^^ :expect 2 @@ -191,12 +191,11 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.190: class Bar extends Foo +//│ ║ l.194: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//│ ═══[WARNING] Cannot yet lift class `Foo` as it is used as a first-class class. //│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. -//│ ═══[RUNTIME ERROR] TypeError: Class extends value [object Object] is not a constructor or null +//│ = Bar { Foo: undefined } // `h` is lifted out, but then cannot access the BlockMemberSymbol M. :fixme @@ -208,7 +207,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.205: module M with +//│ ║ l.208: module M with //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls index 0a103e66bc..61e9af72b6 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NonLocalReturns.mls @@ -6,76 +6,15 @@ fun f() = (() => return 100)() print("Bad") f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ = 100 fun foo(x) = let xs = [() => return x + 1] xs.0() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) foo(123) -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:foo -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res1, $tmp -> tmp, member:Predef -> Predef, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.45: foo(123) -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.26: fun foo(x) = -//│ ║ ^^^^^^^^^^^^ -//│ ║ l.27: let xs = [() => return x + 1] -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.28: xs.0() -//│ ╙── ^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ at REPL10:1:29 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) +//│ = 124 :expect 123 @@ -91,24 +30,8 @@ fun f() = print("Bad") 100 f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ > Ok +//│ = 123 :expect 100 fun f() = @@ -123,85 +46,43 @@ fun f() = print("Continue") 100 f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ > Ok +//│ > 200 +//│ > Continue +//│ = 100 :re fun f() = () => return 3 f()() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_12 :re fun f(): () -> Int = () => return () => 3 f()() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ ═══[RUNTIME ERROR] Error: Unhandled effect $_non$_local$_return$_effect$_14 :e return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.186: return 100 -//│ ╙── ^^^^^^^^^^ +//│ ║ l.67: return 100 +//│ ╙── ^^^^^^^^^^ :e if true do while false do let f = () => return 100 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.194: let f = () => return 100 -//│ ╙── ^^^^^^^^^^ +//│ ║ l.75: let f = () => return 100 +//│ ╙── ^^^^^^^^^^ :e fun f() = type A = return "lol" //│ ╔══[ERROR] Return statements are not allowed in this context. -//│ ║ l.201: type A = return "lol" -//│ ╙── ^^^^^^^^^^^^ +//│ ║ l.82: type A = return "lol" +//│ ╙── ^^^^^^^^^^^^ :expect 100 @@ -216,21 +97,7 @@ fun f() = f() print("Bad") f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ = 100 // broken due to handlers currently not handling `object` properly :fixme @@ -240,8 +107,8 @@ fun f() = return 100 4 f() -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_18 is raised in a constructor +//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' // ctor cannot raise effect, so error is expected. :fixme @@ -251,8 +118,8 @@ fun f() = return 100 new A f() -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_20 is raised in a constructor +//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme :expect 100 @@ -261,8 +128,8 @@ fun f() = (() => return 100)() 4 f() -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_22 is raised in a constructor +//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' :fixme :expect 100 @@ -272,5 +139,5 @@ fun f() = new A 4 f() -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. +//│ ═══[RUNTIME ERROR] Error: Effect $_non$_local$_return$_effect$_24 is raised in a constructor +//│ ═══[RUNTIME ERROR] Expected: '100', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 87d4dc46c7..8dd274d2bf 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -16,21 +16,7 @@ fun f() = h.foo() return 3 // Note: changing this to just `3` leads to `print("Bye!")` executing f() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) +//│ = 3 fun f() = handle h = Effect with @@ -42,64 +28,10 @@ fun f() = h.foo() return 3 10 -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyDefn (Lifter.scala:238) -//│ ═══[WARNING] Cannot yet lift definition `‹non-local return effect›` as it extends an expression. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Unexpected nested class: lambdas may not function correctly. -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:61) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:72) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$lifterReport(HandlerLowering.scala:483) -//│ at: hkmc2.codegen.HandlerLowering$$anon$5.applyDefn(HandlerLowering.scala:524) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:75) -//│ at: hkmc2.codegen.BlockTransformer.applySubBlock(BlockTransformer.scala:16) -//│ at: hkmc2.codegen.BlockTransformer.applyScopedBlock(BlockTransformer.scala:111) -//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:98) -//│ at: hkmc2.codegen.HandlerLowering.translateBlock(HandlerLowering.scala:552) -//│ at: hkmc2.codegen.HandlerLowering.hkmc2$codegen$HandlerLowering$$_$translateFunLike$1(HandlerLowering.scala:510) :expect 3 f() -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:f -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res2, $runtime -> runtime, $tmp -> tmp, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, $Block -> Block, $Shape -> Shape, $block$res -> block$res, member:Effect -> Effect1, class:Effect -> Effect, $block$res -> block$res1, member:Predef -> Predef) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' -//│ ║ l.62: f() -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.35: fun f() = -//│ ║ ^^^^^^^^^ -//│ ║ l.36: handle h = Effect with -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.37: fun foo()(r) = -//│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.38: let m = r() -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ║ l.39: ... (more lines omitted) ... -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: f is not defined -//│ at REPL13:1:29 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:647:22) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' +//│ = 3 :e let l = () => @@ -108,8 +40,8 @@ let l = () => return 3 4 //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.108: return 3 -//│ ╙── ^^^^^^^^ +//│ ║ l.40: return 3 +//│ ╙── ^^^^^^^^ //│ l = fun l // :re @@ -126,7 +58,7 @@ let l = () => 4 l() //│ ╔══[ERROR] Return statements are not allowed outside of functions. -//│ ║ l.125: return 3 -//│ ╙── ^^^^^^^^ +//│ ║ l.57: return 3 +//│ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 98fbe6478b..560b1ffd87 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -11,9 +11,6 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. :expect 14 foo(10, 0) @@ -30,19 +27,19 @@ fun foo(y) = (new M).foo() foo(10) //│ JS (unsanitized): -//│ let M2, foo1, M$; +//│ let M3, foo1, M$; //│ M$ = function M$(isMut, y) { //│ let tmp; //│ if (isMut === true) { -//│ tmp = new M2(y); +//│ tmp = new M3(y); //│ } else { -//│ tmp = globalThis.Object.freeze(new M2(y)); +//│ tmp = globalThis.Object.freeze(new M3(y)); //│ } //│ return tmp //│ }; -//│ (class M1 { +//│ (class M2 { //│ static { -//│ M2 = this +//│ M3 = this //│ } //│ constructor(y) { //│ this.y = y; @@ -60,7 +57,7 @@ foo(10) //│ FAILURE: Unexpected runtime error //│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) //│ ═══[RUNTIME ERROR] TypeError: Cannot assign to read only property 'y' of object '[object Object]' -//│ at M1.foo (REPL16:1:540) +//│ at M2.foo (REPL16:1:540) //│ at foo (REPL16:1:855) //│ at REPL16:1:896 //│ at ContextifyScript.runInThisContext (node:vm:137:12) @@ -108,17 +105,15 @@ fun foo(y) = fun foo() = set y = 2 O -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. let o = foo(10) -//│ o = O +//│ o = O { y: 10, O: undefined } :re set o.x = 1 o.x -//│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' +//│ = 1 +//│ FAILURE: Unexpected lack of runtime error :sjs fun foo(x, y) = @@ -128,116 +123,50 @@ fun foo(x, y) = x + y fun foo3 = M.foo2() foo3 -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. -//│ FAILURE: Unexpected compilation error -//│ FAILURE LOCATION: lookup_! (Scope.scala:114) -//│ FAILURE INFO: Tuple2: -//│ _1 = Tuple2: -//│ _1 = member:M -//│ _2 = class hkmc2.semantics.BlockMemberSymbol -//│ _2 = Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = S of Scope: -//│ parent = N -//│ curThis = S of S of globalThis:globalThis -//│ bindings = HashMap($block$res -> block$res4, class:Capture$foo -> Capture$foo, $runtime -> runtime, $definitionMetadata -> definitionMetadata, $prettyPrint -> prettyPrint, $Term -> Term, member:Capture$foo -> Capture$foo1, $Block -> Block, $Shape -> Shape, member:C$ -> C$, cc -> cc, c0 -> c0, c1 -> c1, $block$res -> block$res5, $selRes -> selRes, $discarded -> discarded, $selRes -> selRes1, $discarded -> discarded1, $block$res -> block$res6, $selRes -> selRes2, $discarded -> discarded2, $block$res -> block$res7, $selRes -> selRes3, $discarded -> discarded3, member:foo -> foo3, $block$res -> block$res, object:O -> O, member:foo -> foo, object:M -> M, $block$res -> block$res8, class:Capture$foo -> Capture$foo2, member:Capture$foo -> Capture$foo3, o -> o, $block$res -> block$res1, $block$res -> block$res9, $block$res -> block$res10, $selRes -> selRes4, $discarded -> discarded4, $block$res -> block$res2, member:M -> M2, member:foo -> foo1, class:M -> M1, member:Predef -> Predef, $block$res -> block$res3, member:M$ -> M$, member:C -> C1, member:foo -> foo2, class:C -> C) -//│ curThis = N -//│ bindings = HashMap(object:M -> M3, member:foo3 -> foo31, member:foo -> foo4) -//│ curThis = S of N -//│ bindings = HashMap() -//│ curThis = N -//│ bindings = HashMap() -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' -//│ ║ l.129: fun foo3 = M.foo2() -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.125: object M with -//│ ║ ^^^^^^ -//│ ║ l.126: fun foo2() = -//│ ║ ^^^^^^^^^^^^^^^^ -//│ ║ l.127: set y = 2 -//│ ║ ^^^^^^^^^^^^^^^ -//│ ║ l.128: x + y -//│ ╙── ^^^^^^^^^^^ //│ JS (unsanitized): -//│ let foo31, foo4; -//│ foo31 = function foo3() { -//│ return M.foo2() +//│ let M5, foo31, foo4, foo3$; +//│ foo3$ = function foo3$(M6) { +//│ return () => { +//│ return foo31(M6) +//│ } //│ }; -//│ foo4 = function foo(x, y) { -//│ let M4, tmp; -//│ (class M3 { -//│ static { -//│ new this -//│ } -//│ constructor() { -//│ M4 = this; -//│ Object.defineProperty(this, "class", { -//│ value: M3 -//│ }); -//│ globalThis.Object.freeze(this); -//│ } -//│ #M$cap; -//│ foo2() { -//│ y = 2; -//│ return x + y -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["object", "M"]; -//│ }); -//│ tmp = foo31(); -//│ return tmp +//│ foo31 = function foo3(M6) { +//│ return M6.foo2() //│ }; -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `M` as it is used as a first-class class. +//│ (class M4 { +//│ static { +//│ M5 = this +//│ } +//│ constructor(x, y, M6) { +//│ this.x = x; +//│ this.y = y; +//│ this.M = M6; +//│ } +//│ #M$cap; +//│ foo2() { +//│ this.y = 2; +//│ return this.x + this.y +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "M"]; +//│ }); +//│ foo4 = function foo(x, y) { let tmp, M$1; M$1 = new M5(x, y, M$1); tmp = foo31(M$1); return tmp }; :expect 12 foo(10, 0) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] ReferenceError: M is not defined -//│ at foo3 (REPL44:1:134) -//│ at foo (REPL44:1:839) -//│ at REPL47:1:39 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' +//│ = 12 data class A(x) with object M with fun getB() = x - fun getA() = M.getB() + fun getA() = + fun bar = M.getB() + bar :expect 2 A(2).getA() -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'getB') -//│ at A.getA (REPL50:1:1161) -//│ at REPL53:1:83 -//│ at ContextifyScript.runInThisContext (node:vm:137:12) -//│ at REPLServer.defaultEval (node:repl:562:24) -//│ at bound (node:domain:433:15) -//│ at REPLServer.runBound [as eval] (node:domain:444:12) -//│ at REPLServer.onLine (node:repl:886:12) -//│ at REPLServer.emit (node:events:508:28) -//│ at REPLServer.emit (node:domain:489:12) -//│ at [_onLine] [as _onLine] (node:internal/readline/interface:465:12) -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: processTerm (JSBackendDiffMaker.scala:210) -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 // Classes and functions in modules // @@ -284,10 +213,10 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ JS (unsanitized): -//│ let M15, tmp3; -//│ (class M14 { +//│ let M17, tmp3; +//│ (class M16 { //│ static { -//│ M15 = this +//│ M17 = this //│ } //│ constructor() { //│ runtime.Unit; @@ -300,18 +229,18 @@ M.A().get //│ }; //│ (class A5 { //│ static { -//│ M14.A.class = this +//│ M16.A.class = this //│ } //│ constructor() {} //│ #A$cap; //│ get get() { -//│ return M15.x + M14.x; +//│ return M17.x + M16.x; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "A", []]; //│ }); -//│ scrut = M14.A(); -//│ if (scrut instanceof M14.A.class) { +//│ scrut = M16.A(); +//│ if (scrut instanceof M16.A.class) { //│ tmp4 = 2; //│ } else { //│ tmp4 = 3; @@ -322,7 +251,7 @@ M.A().get //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); -//│ tmp3 = M15.A(); +//│ tmp3 = M17.A(); //│ tmp3.get //│ = 4 @@ -349,19 +278,7 @@ module M with fun newB = B() 0 is A M.A(2).newB.newA_B(3).newB.get -//│ FAILURE: Unexpected runtime error -//│ FAILURE LOCATION: mkQuery (JSBackendDiffMaker.scala:161) -//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded -//│ at get newA (REPL74:1:1666) -//│ at get newB (REPL74:1:1820) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) -//│ at get newB (REPL74:1:1831) +//│ = 3 module M with class A @@ -374,18 +291,6 @@ fun foo(x) = fun bar = x val baz = x [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. -//│ FAILURE: Unexpected warning -//│ FAILURE LOCATION: applyValue (Lifter.scala:262) -//│ ═══[WARNING] Cannot yet lift class `O` as it is used as a first-class class. foo(123) //│ = [true, [true, false], true] diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index b6e34db602..ebbdb9f985 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -165,7 +165,7 @@ module A with fun g(x) = if x < 0 then 0 else @tailcall f(x) @tailcall g(x - 1) A.f(10000) -//│ ═══[ERROR] Only direct calls in tail position may be marked @tailcall. +//│ ═══[ERROR] This tail call exits the current scope is not optimized. //│ ═══[ERROR] This tail call exits the current scope is not optimized. //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded From 0cad435e31f2e6b5986344b249830c2a6ee9ba2d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 19 Jan 2026 23:59:40 +0800 Subject: [PATCH 18/18] fix unliftable objects --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 9 ++++-- hkmc2/shared/src/test/mlscript/HkScratch.mls | 26 ---------------- hkmc2/shared/src/test/mlscript/HkScratch2.mls | 2 +- .../src/test/mlscript/backlog/Lifter.mls | 14 ++++----- .../test/mlscript/lifter/ModulesObjects.mls | 30 ++++++++++++++----- 5 files changed, 36 insertions(+), 45 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 190805549c..2549b2641d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -336,7 +336,7 @@ class Lifter(topLevelBlk: Block, handlerPaths: HandlerPaths)(using State, Raise, ctx.defnsMap.get(d) match case Some(defnRef) => S(defnRef.read) case None => r.obj match - case c: ScopedObject.Class if c.isObj && r.isInstanceOf[LiftedClass] => + case c: ScopedObject.Class if c.isObj => S(ctx.symbolsMap(c.cls.isym).read) case r: ScopedObject.Referencable[?] => // rewrite the parent @@ -723,8 +723,11 @@ class Lifter(topLevelBlk: Block, handlerPaths: HandlerPaths)(using State, Raise, // BMS refs from ignored defns (including child defns of modules) // Note that we map the DefinitionSymbol to the disambiguated BMS. protected val defnPathsFromThisObj: Map[DefinitionSymbol[?], DefnRef] = - node.children.collect: - case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) if !s.isLifted => + node.children.filter: + case s @ ScopeNode(obj = r: ScopedObject.Class) if r.isObj => false + case _ => true + .collect: + case s @ ScopeNode(obj = r: ScopedObject.Referencable[?]) => !s.isLifted val path = r.owner match case Some(isym) => DefnRef.Field(isym, r.bsym, r.sym) case None => DefnRef.InScope(r.bsym, r.sym) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 04f41a70de..b09a983866 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -11,29 +11,3 @@ class Eff //│ Elab: { Cls Eff { }; } -:lift -:sjs -fun f = - object A - fun foo = A -//│ Elab: { fun member:f‹693› = { Obj A { }; fun member:foo‹692› = member:A‹691›#666; () }; } -//│ JS (unsanitized): -//│ let A1, foo, f, foo$; -//│ foo$ = function foo$(A2) { -//│ return () => { -//│ return foo(A2) -//│ } -//│ }; -//│ foo = function foo(A2) { -//│ return A2 -//│ }; -//│ (class A { -//│ static { -//│ A1 = this -//│ } -//│ constructor() {} -//│ #A$cap; -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "A"]; -//│ }); -//│ f = function f() { let A$; A$ = new A1(); return runtime.Unit }; diff --git a/hkmc2/shared/src/test/mlscript/HkScratch2.mls b/hkmc2/shared/src/test/mlscript/HkScratch2.mls index 8873782395..16a486ebaf 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch2.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch2.mls @@ -20,7 +20,7 @@ module M with A(y) fun newB = B() M.A(2).newB -//│ Elab: { Mod M { Cls AParamList(‹›,List(Param(‹val›,x‹879›,None,Modulefulness(None))),None) { val member:x‹882› = x‹879›#0; Cls BParamList(‹›,List(),None) { method fun member:newA_B‹870›(y‹889›) = { (class:B‹885›#666.)newA_B‹member:newA_B‹870››(2); (module:M‹875›#666.)A‹member:A‹873››(y‹889›#666) }; }; method fun member:newB‹871› = (class:A‹877›#666.)B‹member:B‹872››(); }; }; member:M‹874›#666.A‹member:A‹873››(2).newB } +//│ Elab: { Mod M { Cls AParamList(‹›,List(Param(‹val›,x‹695›,None,Modulefulness(None))),None) { val member:x‹698› = x‹695›#0; Cls BParamList(‹›,List(),None) { method fun member:newA_B‹686›(y‹705›) = { (class:B‹701›#666.)newA_B‹member:newA_B‹686››(2); (module:M‹691›#666.)A‹member:A‹689››(y‹705›#666) }; }; method fun member:newB‹687› = (class:A‹693›#666.)B‹member:B‹688››(); }; }; member:M‹690›#666.A‹member:A‹689››(2).newB } //│ JS: //│ let B1, M1, tmp, B$; //│ B$ = function B$(isMut, A$, A$1, B$1, M2) { diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 35a0b3c4ff..1b2772d1eb 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -140,7 +140,7 @@ fun foo(x, y) = x + y + test M.foo() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.140: module M with +//│ ║ l.136: module M with //│ ╙── ^ :expect 14 @@ -155,7 +155,7 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.155: module M with +//│ ║ l.151: module M with //│ ╙── ^ :expect 12 @@ -168,13 +168,13 @@ data class A(x) with fun getB() = x fun getA() = M.getB() //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.171: module M with +//│ ║ l.167: module M with //│ ╙── ^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'M' //│ ╟── which references the symbol introduced here -//│ ║ l.171: module M with +//│ ║ l.167: module M with //│ ║ ^^^^^^ -//│ ║ l.172: fun getB() = x +//│ ║ l.168: fun getB() = x //│ ╙── ^^^^^^^^^^^^^^^^^^ :expect 2 @@ -191,7 +191,7 @@ fun foo(x) = (new Bar).self2 foo(2) //│ ╔══[ERROR] Expected a statically known class; found reference of type Foo. -//│ ║ l.194: class Bar extends Foo +//│ ║ l.190: class Bar extends Foo //│ ║ ^^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ═══[WARNING] Cannot yet lift definition `Bar` as it extends an expression. @@ -207,7 +207,7 @@ fun f = h M.g //│ ╔══[WARNING] Modules are not yet lifted. -//│ ║ l.208: module M with +//│ ║ l.204: module M with //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 560b1ffd87..1498bb6da0 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -206,6 +206,20 @@ object M with M.f()() //│ = 2 +// unlifted objects +:lift +:w +:expect 2 +fun f = + object A with + fun a = 2 + 0 is A + fun foo = A + foo +f.a +//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. +//│ = 2 + :sjs module M with class A() with @@ -213,7 +227,7 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ JS (unsanitized): -//│ let M17, tmp3; +//│ let M17, tmp4; //│ (class M16 { //│ static { //│ M17 = this @@ -223,11 +237,11 @@ M.A().get //│ } //│ static #M_mod$cap; //│ static { -//│ let scrut, tmp4; +//│ let scrut, tmp5; //│ this.A = function A() { //│ return globalThis.Object.freeze(new A.class()); //│ }; -//│ (class A5 { +//│ (class A6 { //│ static { //│ M16.A.class = this //│ } @@ -241,18 +255,18 @@ M.A().get //│ }); //│ scrut = M16.A(); //│ if (scrut instanceof M16.A.class) { -//│ tmp4 = 2; +//│ tmp5 = 2; //│ } else { -//│ tmp4 = 3; +//│ tmp5 = 3; //│ } -//│ this.x = tmp4; +//│ this.x = tmp5; //│ } //│ #M$cap; //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "M"]; //│ }); -//│ tmp3 = M17.A(); -//│ tmp3.get +//│ tmp4 = M17.A(); +//│ tmp4.get //│ = 4 module M with