From 66edd88e429f41bab32e2eea7fc933cff0ee4aa6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 27 Jan 2025 01:04:28 +0800 Subject: [PATCH 001/127] infrastructure --- .../src/main/scala/hkmc2/MLsCompiler.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 16 ++++++++++++++++ .../src/main/scala/hkmc2/codegen/Lowering.scala | 11 ++++++++--- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 5 +++-- .../src/test/scala/hkmc2/LlirDiffMaker.scala | 2 +- 5 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala index c599c86a5..ac490c4e6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala @@ -74,7 +74,7 @@ class MLsCompiler(preludeFile: os.Path): val parsed = mainParse.resultBlk val (blk, newCtx) = elab.importFrom(parsed) val low = ltl.givenIn: - codegen.Lowering(lowerHandlers = false, stackLimit = None) // TODO: properly hook up stack limit + codegen.Lowering(lowerHandlers = false, stackLimit = None, lift = false) // TODO: properly hook up stack limit val jsb = codegen.js.JSBuilder() val le = low.program(blk) val baseScp: utils.Scope = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala new file mode 100644 index 000000000..ed5bd18d4 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -0,0 +1,16 @@ +package hkmc2 + +import mlscript.utils.*, shorthands.* +import utils.* + +import hkmc2.codegen.* +import hkmc2.semantics.Elaborator.State +import hkmc2.semantics.* + +// Lifts classes and functions to the top-level. +// Assumes the input block does not have any `HandleBlock`s. +class Lifter(using State): + private def lift(f: FunDefn, clsMap: Map[ClassLikeSymbol, ClassLikeSymbol]) = ??? + // val (blk, defns) = f.body.floatOutDefns + + def transform(b: Block) = b \ 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 4c1d9e2f5..72ebbdc85 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -49,7 +49,7 @@ end Subst import Subst.subst -class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, State, Ctx): +class Lowering(lowerHandlers: Bool, stackLimit: Option[Int], lift: Bool)(using TL, Raise, State, Ctx): def returnedTerm(t: st)(using Subst): Block = term(t)(Ret) @@ -507,8 +507,13 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int])(using TL, Raise, St case None => res case Some(lim) => StackSafeTransform(lim).transformTopLevel(res) - if lowerHandlers then HandlerLowering().translateTopLevel(stackSafe) - else stackSafe + val hdlr = + if lowerHandlers then HandlerLowering().translateTopLevel(stackSafe) + else stackSafe + + if lift then Lifter().transform(hdlr) + else hdlr + def program(main: st): Program = def go(acc: Ls[Local -> Str], trm: st): Program = diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 58cc0601a..5c53df047 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -25,6 +25,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val noSanityCheck = NullaryCommand("noSanityCheck") val traceJS = NullaryCommand("traceJS") val handler = NullaryCommand("handler") + val lift = NullaryCommand("lift") val expect = Command("expect"): ln => ln.trim val stackSafe = Command("stackSafe"): ln => @@ -77,7 +78,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case d => outerRaise(d) given Elaborator.Ctx = curCtx val low = ltl.givenIn: - new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit) + new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit, lift = lift.isSet) with codegen.LoweringSelSanityChecks(instrument = false) with codegen.LoweringTraceLog(instrument = false) val jsb = new JSBuilder @@ -92,7 +93,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if js.isSet && !showingJSYieldedCompileError then given Elaborator.Ctx = curCtx val low = ltl.givenIn: - new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit) + new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit, lift = lift.isSet) with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) with codegen.LoweringTraceLog(traceJS.isSet) val jsb = new JSBuilder diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala index 802382911..27b1174a0 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/LlirDiffMaker.scala @@ -37,7 +37,7 @@ abstract class LlirDiffMaker extends BbmlDiffMaker: super.processTerm(trm, inImport) if llir.isSet then val low = ltl.givenIn: - codegen.Lowering(lowerHandlers = false, stackLimit = None) + codegen.Lowering(lowerHandlers = false, stackLimit = None, lift = false) val le = low.program(trm) given Scope = Scope.empty val fresh = Fresh() From 464e5a2fdba81f30fd55a21ae33438d1d92861e7 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 27 Jan 2025 15:59:16 +0800 Subject: [PATCH 002/127] some variable analysis --- .../test/scala/hkmc2/CompileTestRunner.scala | 4 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 77 ++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala index 8c4e0a855..747089e08 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala @@ -52,6 +52,4 @@ class CompileTestRunner compiler.report.badLines.distinct.sorted .map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) -end CompileTestRunner - - +end CompileTestRunner \ No newline at end of file diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index ed5bd18d4..79f702491 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -7,10 +7,83 @@ import hkmc2.codegen.* import hkmc2.semantics.Elaborator.State import hkmc2.semantics.* +import scala.collection.mutable.Set as MutSet +import scala.collection.mutable.Map as MutMap + // Lifts classes and functions to the top-level. // Assumes the input block does not have any `HandleBlock`s. class Lifter(using State): - private def lift(f: FunDefn, clsMap: Map[ClassLikeSymbol, ClassLikeSymbol]) = ??? - // val (blk, defns) = f.body.floatOutDefns + + // Describes the free variables of a function + case class FreeVars(params: Set[Local], bodyVars: Set[Local]) + case class ParamOwner(f: FunDefn, isParam: Bool) + + // use mutable sets locally to avoid reconstructing everything + private case class FreeVarsMut(params: MutSet[Local], bodyVars: MutSet[Local]) + + // Given a function definition f and previously bound locals boundLocals, + // creates a map FunDefn -> List[Local] where each function definitions f + // in boundLocals is associated with a list of locals which both: + // - First occur in f, i.e. is a free variable of f + // - Are accessed by some definition within f + // These are the variables which will be moved to the closure. + // We do this once for every top-level function definition instead + // of once for every function definition so that we only traverse + // the tree once. + private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, ParamOwner]): Map[FunDefn, FreeVarsMut] = + val params: Set[Local] = f.params.flatMap(_.paramSyms).toSet // note: doesn't type check without annotation + val bodyVars = f.body.definedVars -- params + + // add this function's locals to the lookup map + val lookupNext = lookup + ++ params.map(s => (s -> ParamOwner(f, true))) + ++ params.map(s => (s -> ParamOwner(f, false))) + + // collect all function definitions + val vars: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: + case _ -> ParamOwner(f, _) => f -> FreeVarsMut(MutSet(), MutSet()) + ) + + def merge(next: Map[FunDefn, FreeVarsMut]) = + for f -> FreeVarsMut(params, bodyVars) <- next do + for l <- params do vars(f).params.add(l) + for l <- bodyVars do vars(f).bodyVars.add(l) + + def addLocal(l: Local) = lookup.get(l) match + case Some(ParamOwner(f, isParam)) => + if isParam then vars(f).params.add(l) + else vars(f).bodyVars.add(l) + case None => () + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(f: FunDefn, rest) => + merge(findUsedLocalsImpl(f, lookupNext)) + super.applyBlock(b) + case Define(c: ClsLikeDefn, rest) => + for f <- c.methods do merge(findUsedLocalsImpl(f, lookupNext)) + super.applyBlock(b) + case Assign(lhs, _, rest) => + addLocal(lhs) + super.applyBlock(b) + case _ => super.applyBlock(b) + + override def applyValue(v: Value): Value = v match + case Value.Ref(l) => + addLocal(l) + super.applyValue(v) + + case _ => super.applyValue(v) + + walker.applyBlock(f.body) + + vars.toMap + + def findUsedLocals(f: FunDefn) = findUsedLocalsImpl(f, Map()).map: + case f -> FreeVarsMut(params, bodyVars) => f -> FreeVars(params.toSet, bodyVars.toSet) + + private def lift(f: FunDefn, clsMap: Map[ClassLikeSymbol, ClassLikeSymbol]) = + val (blk, defns) = f.body.floatOutDefns + // top-level def transform(b: Block) = b \ No newline at end of file From 9444ad48bd5aebffa4b4b723e72a07fb5db34f39 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 27 Jan 2025 15:59:37 +0800 Subject: [PATCH 003/127] Merge hkmc2 --- .../scala/hkmc2/codegen/HandlerLowering.scala | 4 +- .../hkmc2/codegen/StackSafeTransform.scala | 13 +++-- .../src/test/mlscript-compile/Predef.mjs | 43 ++++++++++---- .../src/test/mlscript-compile/Predef.mls | 16 +++++- .../mlscript/handlers/EffectsInClasses.mls | 3 +- .../src/test/mlscript/handlers/Generators.mls | 13 +++-- .../test/mlscript/handlers/NestedHandlers.mls | 39 +++++++++++-- .../mlscript/handlers/RecursiveHandlers.mls | 3 +- .../test/mlscript/handlers/StackSafety.mls | 56 ++++++++++++------- .../test/mlscript/handlers/ZCombinator.mls | 18 +++--- 10 files changed, 146 insertions(+), 62 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 8f2b8c85e..e5c4b2751 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -68,6 +68,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): private val contClsPath: Path = predefPath.selN(Tree.Ident("__Cont")).selN(Tree.Ident("class")) private val retClsPath: Path = predefPath.selN(Tree.Ident("__Return")).selN(Tree.Ident("class")) private val retClsSym: ClassSymbol = predefSym.tree.definedSymbols.get("__Return").get.asCls.get + private val appendInContPath: Path = predefPath.selN(Tree.Ident("__appendInCont")) private val mkEffectPath: Path = predefPath.selN(Tree.Ident("__mkEffect")) private val handleBlockImplPath: Path = predefPath.selN(Tree.Ident("__handleBlockImpl")) @@ -447,9 +448,8 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): override def applyBlock(b: Block): Block = b match case ReturnCont(res, uid) => blockBuilder - .assignFieldN(res.asPath.tail, nextIdent, clsSym.asPath) .assign(pcSymbol, Value.Lit(Tree.IntLit(uid))) - .ret(res.asPath) + .ret(SimpleCall(appendInContPath, res.asPath :: clsSym.asPath :: Nil)) case StateTransition(uid) => blockBuilder .assign(pcSymbol, Value.Lit(Tree.IntLit(uid))) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index 7baab171d..27ffe60aa 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -35,12 +35,19 @@ class StackSafeTransform(depthLimit: Int)(using State): .ret(res) else val tmp = TempSymbol(None, "tmp") + val offsetGtDepth = TempSymbol(None, "offsetGtDepth") val prevDepth = TempSymbol(None, "prevDepth") blockBuilder .assign(prevDepth, stackDepthPath) .assignFieldN(predefPath, STACK_DEPTH_IDENT, op("+", stackDepthPath, intLit(1))) .assign(tmp, res) .assignFieldN(predefPath, STACK_DEPTH_IDENT, prevDepth.asPath) + .assign(offsetGtDepth, op("<", prevDepth.asPath, stackOffsetPath)) + .ifthen( + offsetGtDepth.asPath, + Case.Lit(Tree.BoolLit(true)), + blockBuilder.assignFieldN(predefPath, STACK_OFFSET_IDENT, prevDepth.asPath).end + ) .rest(f(tmp.asPath)) def extractResTopLevel(res: Result, isTailCall: Bool, f: Result => Block) = @@ -48,7 +55,6 @@ class StackSafeTransform(depthLimit: Int)(using State): val handlerSym = TempSymbol(None, "stackHandler") val resSym = TempSymbol(None, "res") val handlerRes = TempSymbol(None, "res") - val curOffsetSym = TempSymbol(None, "curOffset") val clsSym = ClassSymbol( Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), @@ -63,21 +69,18 @@ class StackSafeTransform(depthLimit: Int)(using State): BlockMemberSymbol("perform", Nil), resumeSym, ParamList(ParamListFlags.empty, Nil, N) :: Nil, /* fun perform() = - let curOffset = stackOffset stackOffset = stackDepth let ret = resume() - stackOffset = curOffset ret */ blockBuilder - .assign(curOffsetSym, stackOffsetPath) .assignFieldN(predefPath, STACK_OFFSET_IDENT, stackDepthPath) .assign(handlerRes, Call(Value.Ref(resumeSym), Nil)(true)) - .assignFieldN(predefPath, STACK_OFFSET_IDENT, curOffsetSym.asPath) .ret(handlerRes.asPath) ) :: Nil, blockBuilder .assignFieldN(predefPath, STACK_LIMIT_IDENT, intLit(depthLimit)) // set stackLimit before call + .assignFieldN(predefPath, STACK_OFFSET_IDENT, intLit(0)) // set stackOffset = 0 before call .assignFieldN(predefPath, STACK_DEPTH_IDENT, intLit(1)) // set stackDepth = 1 before call .assignFieldN(predefPath, STACK_HANDLER_IDENT, handlerSym.asPath) // assign stack handler .rest(HandleBlockReturn(res)), diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index 983aca7c0..32fcc7972 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -78,12 +78,12 @@ const Predef$class = class Predef { } toString() { return "__Cont(" + this.next + ")"; } }; - this.__List = function __List(next1) { return new __List.class(next1); }; - this.__List.class = class __List { + this.__TailList = function __TailList(next1) { return new __TailList.class(next1); }; + this.__TailList.class = class __TailList { constructor(next) { this.next = next; } - toString() { return "__List(" + this.next + ")"; } + toString() { return "__TailList(" + this.next + ")"; } }; this.__ListWithTail = function __ListWithTail(next1, tail1) { return new __ListWithTail.class(next1, tail1); }; this.__ListWithTail.class = class __ListWithTail { @@ -321,6 +321,25 @@ const Predef$class = class Predef { res.tail = res; return res; } + __appendInCont(eff, cont) { + let scrut, scrut1, tmp, tmp1; + scrut = eff.tail; + if (scrut instanceof this.__TailList.class) { + scrut1 = cont.next !== null; + if (scrut1 === true) { + throw globalThis.Error("unexpected handler continuation"); + } else { + tmp = null; + } + cont.next = eff.tail.next; + eff.tail.next = cont; + tmp1 = null; + } else { + eff.tail.next = cont; + tmp1 = null; + } + return eff; + } __mkEffect(handler, handlerFun) { let res, tmp, tmp1; tmp = this.__mkListWithTail(); @@ -331,7 +350,7 @@ const Predef$class = class Predef { } __handleBlockImpl(cur, handler1) { let handleBlock, nxt, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; - tmp = this.__List(null); + tmp = this.__TailList(null); tmp1 = new this.__HandleBlock.class(tmp, null, null, handler1); handleBlock = tmp1; tmp2 = cur.handleBlockList.append(handleBlock) ?? null; @@ -420,7 +439,7 @@ const Predef$class = class Predef { } __resume(cur2, tail) { return (value) => { - let scrut, cont, scrut1, scrut2, scrut3, scrut4, scrut5, scrut6, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; + let scrut, cont1, scrut1, scrut2, scrut3, scrut4, scrut5, scrut6, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; scrut = cur2.resumed; if (scrut === true) { throw globalThis.Error("Multiple resumption"); @@ -428,15 +447,15 @@ const Predef$class = class Predef { tmp = null; } cur2.resumed = true; - cont = cur2.next; + cont1 = cur2.next; tmp10: while (true) { - if (cont instanceof this.__Cont.class) { - tmp1 = cont.resume(value) ?? null; + if (cont1 instanceof this.__Cont.class) { + tmp1 = cont1.resume(value) ?? null; value = tmp1; if (value instanceof this.__EffectSig.class) { - scrut1 = value.tail.next !== cont; + scrut1 = value.tail.next !== cont1; if (scrut1 === true) { - scrut2 = cont.next !== null; + scrut2 = cont1.next !== null; if (scrut2 === true) { scrut3 = value.tail.next !== null; if (scrut3 === true) { @@ -453,7 +472,7 @@ const Predef$class = class Predef { } scrut4 = value.tail.next === null; if (scrut4 === true) { - value.tail.next = cont.next; + value.tail.next = cont1.next; tmp4 = null; } else { tmp4 = null; @@ -469,7 +488,7 @@ const Predef$class = class Predef { } return value; } else { - cont = cont.next; + cont1 = cont1.next; tmp6 = null; } tmp7 = tmp6; diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mls b/hkmc2/shared/src/test/mlscript-compile/Predef.mls index 2b9bde9df..ee4406823 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mls @@ -118,7 +118,7 @@ class Test with abstract class __Cont(next) with fun resume(value) -class __List(next) +class __TailList(next) class __ListWithTail(next, tail) with fun append(elem) = @@ -134,13 +134,25 @@ class __HandleBlock(contHead, lastHandlerCont, next, handler) class __EffectSig(next, tail, handleBlockList, resumed, handler, handlerFun) class __Return(value) +fun __appendInCont(eff, cont) = + if eff.tail is __TailList then + // do prepend instead + if cont.next !== null do + throw Error("unexpected handler continuation") + set + cont.next = eff.tail.next + eff.tail.next = cont + else + set eff.tail.next = cont + eff + fun __mkEffect(handler, handlerFun) = let res = new __EffectSig(null, null, __mkListWithTail(), false, handler, handlerFun) set res.tail = res res fun __handleBlockImpl(cur, handler) = - let handleBlock = new __HandleBlock(__List(null), null, null, handler) + let handleBlock = new __HandleBlock(__TailList(null), null, null, handler) cur.handleBlockList.append(handleBlock) while cur is __EffectSig then diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index d417f13bd..537b5f5c0 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -37,9 +37,8 @@ class Lol(h) with //│ tmp = res; //│ res1 = Predef.print(tmp); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = this; //│ this.pc = 1; -//│ return res1; +//│ return globalThis.Predef.__appendInCont(res1, this); //│ } //│ this.pc = 1; //│ continue contLoop; diff --git a/hkmc2/shared/src/test/mlscript/handlers/Generators.mls b/hkmc2/shared/src/test/mlscript/handlers/Generators.mls index 75acb3fd8..7ca86ccdf 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Generators.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Generators.mls @@ -61,8 +61,7 @@ fun permutations_impl(gen, l1, l2) = [f, ...t] do handle genWithPrefix = Generator with fun produce(result)(resume) = - result.unshift(f) - gen.produce(result) + gen.produce([f, ...result]) let x = resume(()) x permutations_impl(genWithPrefix, [], l1.concat(t)) @@ -73,7 +72,6 @@ fun permutations_impl(gen, l1, l2) = fun permutations(gen, l) = permutations_impl(gen, [], l) -// FIXME: wrong code let res = [] handle gen = Generator with fun produce(result)(resume) = @@ -81,4 +79,11 @@ handle gen = Generator with let x = resume(()) x in permutations(gen, [1, 2, 3]) -//│ res = [ [ 1, 2, 3 ], [ 1, 3, 2 ] ] +//│ > [ +//│ > [ 1, 2, 3 ], +//│ > [ 1, 3, 2 ], +//│ > [ 2, 1, 3 ], +//│ > [ 2, 3, 1 ], +//│ > [ 3, 1, 2 ], +//│ > [ 3, 2, 1 ] +//│ res = ] diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 6ad05ca19..cdf4b21ae 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -96,7 +96,7 @@ let box = Box(0) :expect 5120 fun f(h, box, n) = - if n <= 1 then + if n <= 1 then h.perform() else handle h2 = Eff with @@ -114,18 +114,47 @@ f(h, box, 10) box.n //│ = 5120 +handle h1 = Eff with + fun perform()(k) = + print("h1") + let x = k() + print("h1 end") + x +handle h2 = Eff with + fun perform()(k) = + print("h2") + h1.perform() + let x = k() + print("h2 end") + x +h2.perform() +h2.perform() +h2.perform() +//│ > h2 +//│ > h1 +//│ > h2 +//│ > h1 +//│ > h2 +//│ > h1 +//│ > h2 end +//│ > h2 end +//│ > h2 end +//│ > h1 end +//│ > h1 end +//│ > h1 end + handle h1 = Eff with - fun perform()(k) = + fun perform()(k) = print("h1") k() handle h2 = Eff with - fun perform()(k) = + fun perform()(k) = print("h2") h1.perform() k() handle h3 = Eff with - fun perform()(k) = + fun perform()(k) = print("h3") h2.perform() k() @@ -151,7 +180,7 @@ h1.perform() while i < 10 do handle h2 = Eff with fun perform()(k) = - set d = d + 1 + set d = d + 1 k() h2.perform() set i = i + 1 diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 88dc9733e..4b4895119 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -272,9 +272,8 @@ str //│ tmp9 = res5; //│ res6 = h1.perform(null) ?? null; //│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { -//│ res6.tail.next = this; //│ this.pc = 1; -//│ return res6; +//│ return globalThis.Predef.__appendInCont(res6, this); //│ } //│ this.pc = 1; //│ continue contLoop; diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index e6444f1ce..e7ead6ded 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -89,7 +89,7 @@ hi(0) //│ } //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { -//│ let res2, curOffset, res3, Cont$1; +//│ let res2, res3, Cont$1; //│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { @@ -104,7 +104,6 @@ hi(0) //│ contLoop: while (true) { //│ if (this.pc === 4) { //│ res2 = res3; -//│ globalThis.Predef.__stackOffset = curOffset; //│ return res2; //│ } //│ break; @@ -112,7 +111,6 @@ hi(0) //│ } //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; -//│ curOffset = globalThis.Predef.__stackOffset; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res3 = resume(); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { @@ -121,7 +119,6 @@ hi(0) //│ return res3; //│ } //│ res2 = res3; -//│ globalThis.Predef.__stackOffset = curOffset; //│ return res2; //│ }); //│ } @@ -149,6 +146,7 @@ hi(0) //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; //│ globalThis.Predef.__stackLimit = 5; +//│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; //│ res1 = hi1(0); @@ -179,7 +177,7 @@ sum(10000) //│ JS (unsanitized): //│ let sum3, res1, handleBlock$3; //│ sum3 = function sum(n) { -//│ let scrut, tmp, tmp1, tmp2, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$; +//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$; //│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { @@ -194,7 +192,7 @@ sum(10000) //│ res2 = value$; //│ } //│ contLoop: while (true) { -//│ if (this.pc === 3) { +//│ if (this.pc === 4) { //│ scrut = n == 0; //│ if (scrut === true) { //│ return 0; @@ -204,9 +202,8 @@ sum(10000) //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; //│ res3 = sum3(tmp); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = this; //│ this.pc = 1; -//│ return res3; +//│ return globalThis.Predef.__appendInCont(res3, this); //│ } //│ this.pc = 1; //│ continue contLoop; @@ -218,11 +215,20 @@ sum(10000) //│ } else if (this.pc === 1) { //│ tmp2 = res3; //│ globalThis.Predef.__stackDepth = prevDepth; +//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; +//│ if (offsetGtDepth === true) { +//│ globalThis.Predef.__stackOffset = prevDepth; +//│ this.pc = 3; +//│ continue contLoop; +//│ } +//│ this.pc = 3; +//│ continue contLoop; +//│ } else if (this.pc === 3) { //│ tmp1 = tmp2; //│ return n + tmp1; //│ } else if (this.pc === 0) { //│ dummy = res2; -//│ this.pc = 3; +//│ this.pc = 4; //│ continue contLoop; //│ } //│ break; @@ -258,6 +264,10 @@ sum(10000) //│ } //│ tmp2 = res3; //│ globalThis.Predef.__stackDepth = prevDepth; +//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; +//│ if (offsetGtDepth === true) { +//│ globalThis.Predef.__stackOffset = prevDepth; +//│ } //│ tmp1 = tmp2; //│ return n + tmp1; //│ } @@ -271,7 +281,7 @@ sum(10000) //│ } //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { -//│ let res3, curOffset, res4, Cont$1; +//│ let res3, res4, Cont$1; //│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { @@ -280,13 +290,12 @@ sum(10000) //│ this.pc = pc; //│ } //│ resume(value$) { -//│ if (this.pc === 5) { +//│ if (this.pc === 6) { //│ res4 = value$; //│ } //│ contLoop: while (true) { -//│ if (this.pc === 5) { +//│ if (this.pc === 6) { //│ res3 = res4; -//│ globalThis.Predef.__stackOffset = curOffset; //│ return res3; //│ } //│ break; @@ -294,16 +303,14 @@ sum(10000) //│ } //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; -//│ curOffset = globalThis.Predef.__stackOffset; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res4 = resume(); //│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = new Cont$1.class(5); +//│ res4.tail.next = new Cont$1.class(6); //│ res4.tail = res4.tail.next; //│ return res4; //│ } //│ res3 = res4; -//│ globalThis.Predef.__stackOffset = curOffset; //│ return res3; //│ }); //│ } @@ -318,11 +325,11 @@ sum(10000) //│ this.pc = pc; //│ } //│ resume(value$) { -//│ if (this.pc === 4) { +//│ if (this.pc === 5) { //│ res2 = value$; //│ } //│ contLoop: while (true) { -//│ if (this.pc === 4) { +//│ if (this.pc === 5) { //│ return res2; //│ } //│ break; @@ -331,11 +338,12 @@ sum(10000) //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; //│ globalThis.Predef.__stackLimit = 1000; +//│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; //│ res2 = sum3(10000); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = new Cont$(4); +//│ res2.tail.next = new Cont$(5); //│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler); //│ } //│ return res2; @@ -476,3 +484,13 @@ hi(0) //│ ═══[RUNTIME ERROR] Error: This code requires effect handler instrumentation but was compiled without it. +:stackSafe 1000 +:handler +:expect 100010000 +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +fun bad() = sum(10000) + sum(10000) +bad() +//│ = 100010000 diff --git a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls index 7d7cb4122..7bfda2dc3 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls @@ -23,7 +23,7 @@ fact(10) fact(10000) //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded - +:stackSafe 1000 fun mkrec(g) = selfApp of self => g of y => selfApp(self)(y) @@ -93,14 +93,14 @@ let fact = fact(10) //│ > 2 0 //│ > 4 0 -//│ > 6 5 -//│ > 8 5 -//│ > 10 10 -//│ > 12 10 -//│ > 14 10 -//│ > 16 15 -//│ > 18 15 -//│ > 20 20 +//│ > 6 4 +//│ > 8 4 +//│ > 10 8 +//│ > 12 8 +//│ > 14 12 +//│ > 16 12 +//│ > 18 16 +//│ > 20 16 //│ > resumed: 1 //│ > resumed: 2 //│ > resumed: 3 From 3e59e8e0fcf635ae8afb5efac0d28b615375073e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 27 Jan 2025 17:48:26 +0800 Subject: [PATCH 004/127] create closure class --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 94 ++++++++++++++++--- .../src/test/mlscript/codegen/Lifter.mls | 51 ++++++++++ 2 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/codegen/Lifter.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 79f702491..4acfaa30f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -4,14 +4,17 @@ import mlscript.utils.*, shorthands.* import utils.* import hkmc2.codegen.* -import hkmc2.semantics.Elaborator.State import hkmc2.semantics.* +import hkmc2.semantics.Elaborator.State +import hkmc2.syntax.Tree +import hkmc2.codegen.llir.FreshInt import scala.collection.mutable.Set as MutSet import scala.collection.mutable.Map as MutMap // Lifts classes and functions to the top-level. -// Assumes the input block does not have any `HandleBlock`s. +// Assumes the input block does not have any `HandleBlock`s and lamdbas are +// rewritten as functions (lambdas will be removed from the IR soon). class Lifter(using State): // Describes the free variables of a function @@ -20,6 +23,8 @@ class Lifter(using State): // use mutable sets locally to avoid reconstructing everything private case class FreeVarsMut(params: MutSet[Local], bodyVars: MutSet[Local]) + + type UsedLocalsMap = Map[FunDefn, FreeVars] // Given a function definition f and previously bound locals boundLocals, // creates a map FunDefn -> List[Local] where each function definitions f @@ -31,23 +36,33 @@ class Lifter(using State): // of once for every function definition so that we only traverse // the tree once. private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, ParamOwner]): Map[FunDefn, FreeVarsMut] = - val params: Set[Local] = f.params.flatMap(_.paramSyms).toSet // note: doesn't type check without annotation - val bodyVars = f.body.definedVars -- params + val params = f.params.flatMap(_.paramSyms).toSet + val bodyVars = (f.body.definedVars -- params).collect: + case s: FlowSymbol => s + + println(f.sym) + println(params) + println(bodyVars) // add this function's locals to the lookup map val lookupNext = lookup ++ params.map(s => (s -> ParamOwner(f, true))) - ++ params.map(s => (s -> ParamOwner(f, false))) + ++ bodyVars.map(s => (s -> ParamOwner(f, false))) // collect all function definitions val vars: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: case _ -> ParamOwner(f, _) => f -> FreeVarsMut(MutSet(), MutSet()) ) + // add this function in case this function has no locals + if !vars.contains(f) then vars.addOne(f -> FreeVarsMut(MutSet(), MutSet())) + def merge(next: Map[FunDefn, FreeVarsMut]) = - for f -> FreeVarsMut(params, bodyVars) <- next do - for l <- params do vars(f).params.add(l) - for l <- bodyVars do vars(f).bodyVars.add(l) + for f -> (v @ FreeVarsMut(params, bodyVars)) <- next do vars.get(f) match + case None => vars.addOne(f -> v) + case Some(value) => + for l <- params do vars(f).params.add(l) + for l <- bodyVars do vars(f).bodyVars.add(l) def addLocal(l: Local) = lookup.get(l) match case Some(ParamOwner(f, isParam)) => @@ -79,11 +94,64 @@ class Lifter(using State): vars.toMap - def findUsedLocals(f: FunDefn) = findUsedLocalsImpl(f, Map()).map: - case f -> FreeVarsMut(params, bodyVars) => f -> FreeVars(params.toSet, bodyVars.toSet) - - private def lift(f: FunDefn, clsMap: Map[ClassLikeSymbol, ClassLikeSymbol]) = + def findUsedLocals(b: Block): UsedLocalsMap = + var usedMap: UsedLocalsMap = Map() + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(f: FunDefn, rest) => + val m = findUsedLocalsImpl(f, Map()).map: + case f -> FreeVarsMut(params, bodyVars) => f -> FreeVars(params.toSet, bodyVars.toSet) + usedMap ++= m + super.applyBlock(b) + case _ => super.applyBlock(b) + walker.applyBlock(b) + usedMap + + def createClosureCls(f: FunDefn)(using usedMap: UsedLocalsMap) = + val nme = f.sym.nme + "$closure" + + val clsSym = ClassSymbol( + Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), + Tree.Ident(nme) + ) + + val FreeVars(paramVars, bodyVars) = usedMap(f) + val vars = paramVars ++ bodyVars + + val fresh = FreshInt() + + val varsMap: Map[Local, VarSymbol] = vars.map(s => + val id = fresh.make + s -> VarSymbol(Tree.Ident(s.nme + id + "$")) + ).toMap + + val defn = ClsLikeDefn( + None, clsSym, BlockMemberSymbol(nme, Nil), + syntax.Cls, + S(PlainParamList(vars.toList.map(s => Param(FldFlags.empty, varsMap(s), None)))), + None, Nil, Nil, Nil, End(), End() + ) + + (defn, varsMap) + + + private def lift(f: FunDefn) = val (blk, defns) = f.body.floatOutDefns // top-level - def transform(b: Block) = b \ No newline at end of file + def transform(b: Block) = + given usedMap: UsedLocalsMap = findUsedLocals(b) + // for debugging + println(usedMap.map: + case a -> b => a.sym -> b + ) + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(f: FunDefn, rest) => + Define(createClosureCls(f)._1, Define(f, applyBlock(rest))) + case _ => super.applyBlock(b) + walker.applyBlock(b) + + + \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls new file mode 100644 index 000000000..1f903f70e --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -0,0 +1,51 @@ +:lift +:js + +:sjs +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test() with + fun get() = used2 + used1 + Test() +f(1, 2).get() +//│ JS (unsanitized): +//│ let f1, tmp, f$closure1; +//│ f$closure1 = function f$closure(used10$1, used21$1) { return new f$closure.class(used10$1, used21$1); }; +//│ f$closure1.class = class f$closure { +//│ constructor(used10$, used21$) { +//│ this.used10$ = used10$; +//│ this.used21$ = used21$; +//│ } +//│ toString() { return "f$closure(" + this.used10$ + ", " + this.used21$ + ")"; } +//│ }; +//│ f1 = function f(used1, unused1) { +//│ let Test, g, used2, unused2; +//│ g = function g(g$_arg) { +//│ let h, used3, tmp1; +//│ h = function h() { +//│ return used3; +//│ }; +//│ used3 = 2; +//│ tmp1 = h(); +//│ return used1 + tmp1; +//│ }; +//│ used2 = unused1; +//│ unused2 = 2; +//│ Test = function Test() { return new Test.class(); }; +//│ Test.class = class Test { +//│ constructor() {} +//│ get() { +//│ return used2 + used1; +//│ } +//│ toString() { return "Test(" + + ")"; } +//│ }; +//│ return Test(); +//│ }; +//│ tmp = f1(1, 2); +//│ tmp.get() ?? null +//│ = 3 From 0d56edf8a87a1336c95ba124d4822f86618ea955 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 27 Jan 2025 19:16:36 +0800 Subject: [PATCH 005/127] add infrastructure to later distinguish mutable variables --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 67 ++++++++----------- .../src/test/mlscript/codegen/Lifter.mls | 3 + 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 4acfaa30f..190a7484f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -17,12 +17,13 @@ import scala.collection.mutable.Map as MutMap // rewritten as functions (lambdas will be removed from the IR soon). class Lifter(using State): - // Describes the free variables of a function - case class FreeVars(params: Set[Local], bodyVars: Set[Local]) - case class ParamOwner(f: FunDefn, isParam: Bool) + // Describes the free variables of a function. + // vars: The free variables that are accessed or mutated by nested classes/functions. + // mutated: The free variables that are mutated, but not accessed, by nested classes/functions. + case class FreeVars(vars: Set[Local], mutated: Set[Local]) // use mutable sets locally to avoid reconstructing everything - private case class FreeVarsMut(params: MutSet[Local], bodyVars: MutSet[Local]) + private case class FreeVarsMut(vars: MutSet[Local], mutated: MutSet[Local]) type UsedLocalsMap = Map[FunDefn, FreeVars] @@ -35,39 +36,34 @@ class Lifter(using State): // We do this once for every top-level function definition instead // of once for every function definition so that we only traverse // the tree once. - private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, ParamOwner]): Map[FunDefn, FreeVarsMut] = - val params = f.params.flatMap(_.paramSyms).toSet - val bodyVars = (f.body.definedVars -- params).collect: + private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[FunDefn, FreeVarsMut] = + val definedVars = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: case s: FlowSymbol => s - println(f.sym) - println(params) - println(bodyVars) + println(definedVars) // add this function's locals to the lookup map - val lookupNext = lookup - ++ params.map(s => (s -> ParamOwner(f, true))) - ++ bodyVars.map(s => (s -> ParamOwner(f, false))) + val lookupNext = lookup ++ definedVars.map(_ -> f) // collect all function definitions - val vars: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> ParamOwner(f, _) => f -> FreeVarsMut(MutSet(), MutSet()) + val retMap: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: + case _ -> f => f -> FreeVarsMut(MutSet(), MutSet()) ) // add this function in case this function has no locals - if !vars.contains(f) then vars.addOne(f -> FreeVarsMut(MutSet(), MutSet())) + if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(MutSet(), MutSet())) def merge(next: Map[FunDefn, FreeVarsMut]) = - for f -> (v @ FreeVarsMut(params, bodyVars)) <- next do vars.get(f) match - case None => vars.addOne(f -> v) + for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match + case None => retMap.addOne(f -> v) case Some(value) => - for l <- params do vars(f).params.add(l) - for l <- bodyVars do vars(f).bodyVars.add(l) + for l <- vars do retMap(f).vars.add(l) + for l <- mutated do retMap(f).mutated.add(l) - def addLocal(l: Local) = lookup.get(l) match - case Some(ParamOwner(f, isParam)) => - if isParam then vars(f).params.add(l) - else vars(f).bodyVars.add(l) + def addLocal(l: Local, mut: Bool) = lookup.get(l) match + case Some(f) => + if mut then retMap(f).mutated.add(l) + retMap(f).vars.add(l) case None => () val walker = new BlockTransformerShallow(SymbolSubst()): @@ -79,20 +75,20 @@ class Lifter(using State): for f <- c.methods do merge(findUsedLocalsImpl(f, lookupNext)) super.applyBlock(b) case Assign(lhs, _, rest) => - addLocal(lhs) + addLocal(lhs, true) // TODO: for now, we just assume if a symbol is assigned to, then it's mutable super.applyBlock(b) case _ => super.applyBlock(b) override def applyValue(v: Value): Value = v match case Value.Ref(l) => - addLocal(l) + addLocal(l, false) super.applyValue(v) case _ => super.applyValue(v) walker.applyBlock(f.body) - vars.toMap + retMap.toMap def findUsedLocals(b: Block): UsedLocalsMap = var usedMap: UsedLocalsMap = Map() @@ -100,7 +96,8 @@ class Lifter(using State): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => val m = findUsedLocalsImpl(f, Map()).map: - case f -> FreeVarsMut(params, bodyVars) => f -> FreeVars(params.toSet, bodyVars.toSet) + case f -> FreeVarsMut(vars, mutated) => + f -> FreeVars(vars.toSet, mutated.toSet) usedMap ++= m super.applyBlock(b) case _ => super.applyBlock(b) @@ -115,8 +112,7 @@ class Lifter(using State): Tree.Ident(nme) ) - val FreeVars(paramVars, bodyVars) = usedMap(f) - val vars = paramVars ++ bodyVars + val FreeVars(vars, mutated) = usedMap(f) val fresh = FreshInt() @@ -135,23 +131,16 @@ class Lifter(using State): (defn, varsMap) - private def lift(f: FunDefn) = + private def liftFn(f: FunDefn) = val (blk, defns) = f.body.floatOutDefns // top-level def transform(b: Block) = given usedMap: UsedLocalsMap = findUsedLocals(b) - // for debugging - println(usedMap.map: - case a -> b => a.sym -> b - ) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => Define(createClosureCls(f)._1, Define(f, applyBlock(rest))) case _ => super.applyBlock(b) - walker.applyBlock(b) - - - \ No newline at end of file + walker.applyBlock(b) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 1f903f70e..d16be7905 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -49,3 +49,6 @@ f(1, 2).get() //│ tmp = f1(1, 2); //│ tmp.get() ?? null //│ = 3 + //│ f$closure1 = function f$closure(used20$1) { return new f$closure.class(used20$1); }; +//│ JS (unsanitized): +//│ null From 2ff423bd95591c3dfeedf18d6554a2288c2c38e0 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 01:55:10 +0800 Subject: [PATCH 006/127] Function lifting working --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 210 +++++++++++++++--- .../src/test/mlscript/codegen/Lifter.mls | 133 ++++++++--- 2 files changed, 280 insertions(+), 63 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 190a7484f..8b15e3906 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -9,7 +9,7 @@ import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt -import scala.collection.mutable.Set as MutSet +import scala.collection.mutable.ListBuffer as ListBuffer import scala.collection.mutable.Map as MutMap // Lifts classes and functions to the top-level. @@ -20,50 +20,101 @@ class Lifter(using State): // Describes the free variables of a function. // vars: The free variables that are accessed or mutated by nested classes/functions. // mutated: The free variables that are mutated, but not accessed, by nested classes/functions. - case class FreeVars(vars: Set[Local], mutated: Set[Local]) + case class FreeVars(vars: List[Local], mutated: List[Local]) // use mutable sets locally to avoid reconstructing everything - private case class FreeVarsMut(vars: MutSet[Local], mutated: MutSet[Local]) + private case class FreeVarsMut(vars: ListBuffer[Local], mutated: ListBuffer[Local]) - type UsedLocalsMap = Map[FunDefn, FreeVars] + class UsedLocalsMap(mp: Map[BlockMemberSymbol, FreeVars]): + def apply(f: BlockMemberSymbol) = mp(f) + private lazy val inverse = mp.flatMap: + case fn -> vars => vars.vars.map(v => v -> fn) + // gets the function to which a local belongs + def lookup(l: Local) = inverse.get(l) + def print = println(mp.map: + case a -> b => a -> b + ) + + object UsedLocalsMap: + def from(mp: Map[FunDefn, FreeVars]) = + UsedLocalsMap(mp.map: + case a -> b => a.sym -> b + ) + + + class LifterCtx( + val usedLocals: UsedLocalsMap, + val localSyms: Map[Local, VarSymbol], + val prevDefns: List[FunDefn], + val capturePaths: Map[BlockMemberSymbol, Path], + val bmsReqdCaptures: Map[BlockMemberSymbol, List[BlockMemberSymbol]], // required captures + val bmsPaths: Map[BlockMemberSymbol, Path], + ): + // gets the function to which a local belongs + def lookup(l: Local) = usedLocals.lookup(l) + // the path to access the capture of a particular function + def getCapturePath(b: BlockMemberSymbol) = capturePaths.get(b) + // the path to access the capture of the function that a local belongs to + def getLocalClosPath(l: Local) = lookup(l).flatMap(capturePaths.get(_)) + // the symbol in the capture corresponding to a particular local + def getLocalSym(l: Local) = localSyms(l) + // the path to a local value containing this function with the captures already applied + def getBmsPath(b: BlockMemberSymbol) = bmsPaths.get(b) + + def addDefn(f: FunDefn) = + LifterCtx(usedLocals, localSyms, f :: prevDefns, capturePaths, bmsReqdCaptures, bmsPaths) + def addLocalPaths(m: Map[Local, VarSymbol]) = + LifterCtx(usedLocals, localSyms ++ m, prevDefns, capturePaths, bmsReqdCaptures, bmsPaths) + def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = + LifterCtx(usedLocals, localSyms, prevDefns, paths, bmsReqdCaptures, bmsPaths) + def addCapturePath(src: BlockMemberSymbol, path: Path) = + LifterCtx(usedLocals, localSyms, prevDefns, capturePaths + (src -> path), bmsReqdCaptures, bmsPaths) + def addReqdCaptures(mp: Map[BlockMemberSymbol, List[BlockMemberSymbol]]) = + LifterCtx(usedLocals, localSyms, prevDefns, capturePaths, bmsReqdCaptures ++ mp, bmsPaths) + def addBmsPaths(paths: Map[BlockMemberSymbol, Path]) = + LifterCtx(usedLocals, localSyms, prevDefns, capturePaths, bmsReqdCaptures, bmsPaths ++ paths) + + def getVars(f: FunDefn): Set[Local] = + (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: + case s: FlowSymbol => s // Given a function definition f and previously bound locals boundLocals, // creates a map FunDefn -> List[Local] where each function definitions f // in boundLocals is associated with a list of locals which both: // - First occur in f, i.e. is a free variable of f // - Are accessed by some definition within f - // These are the variables which will be moved to the closure. + // These are the variables which will be moved to the capture. // We do this once for every top-level function definition instead // of once for every function definition so that we only traverse // the tree once. private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[FunDefn, FreeVarsMut] = - val definedVars = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: - case s: FlowSymbol => s - - println(definedVars) + val definedVars = getVars(f) // add this function's locals to the lookup map - val lookupNext = lookup ++ definedVars.map(_ -> f) + // NOTE: `lookup` will overwrite definitions already defined in previous functions + // here, ++ must not be used as a commutative operator! + val lookupNext = definedVars.map(_ -> f).toMap ++ lookup // collect all function definitions val retMap: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> f => f -> FreeVarsMut(MutSet(), MutSet()) + case _ -> f => f -> FreeVarsMut(ListBuffer.empty, ListBuffer.empty) ) // add this function in case this function has no locals - if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(MutSet(), MutSet())) + if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(ListBuffer.empty, ListBuffer.empty)) + // merge recursive call results def merge(next: Map[FunDefn, FreeVarsMut]) = for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match case None => retMap.addOne(f -> v) case Some(value) => - for l <- vars do retMap(f).vars.add(l) - for l <- mutated do retMap(f).mutated.add(l) + for l <- vars do retMap(f).vars.addOne(l) + for l <- mutated do retMap(f).mutated.addOne(l) def addLocal(l: Local, mut: Bool) = lookup.get(l) match case Some(f) => - if mut then retMap(f).mutated.add(l) - retMap(f).vars.add(l) + if mut then retMap(f).mutated.addOne(l) + retMap(f).vars.addOne(l) case None => () val walker = new BlockTransformerShallow(SymbolSubst()): @@ -91,56 +142,155 @@ class Lifter(using State): retMap.toMap def findUsedLocals(b: Block): UsedLocalsMap = - var usedMap: UsedLocalsMap = Map() + var usedMap: Map[FunDefn, FreeVars] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - val m = findUsedLocalsImpl(f, Map()).map: + val m = findUsedLocalsImpl(f, Map.empty).map: case f -> FreeVarsMut(vars, mutated) => - f -> FreeVars(vars.toSet, mutated.toSet) + f -> FreeVars(vars.toList, mutated.toList) usedMap ++= m super.applyBlock(b) case _ => super.applyBlock(b) walker.applyBlock(b) - usedMap + UsedLocalsMap.from(usedMap) - def createClosureCls(f: FunDefn)(using usedMap: UsedLocalsMap) = - val nme = f.sym.nme + "$closure" + def createCaptureCls(f: FunDefn, ctx: LifterCtx) = + val nme = f.sym.nme + "$capture" val clsSym = ClassSymbol( Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), Tree.Ident(nme) ) - val FreeVars(vars, mutated) = usedMap(f) + val FreeVars(vars, mutated) = ctx.usedLocals(f.sym) val fresh = FreshInt() - val varsMap: Map[Local, VarSymbol] = vars.map(s => + val varsMap: Map[Local, VarSymbol] = vars.map: s => val id = fresh.make s -> VarSymbol(Tree.Ident(s.nme + id + "$")) - ).toMap + .toMap + + val varsList = vars.toList val defn = ClsLikeDefn( None, clsSym, BlockMemberSymbol(nme, Nil), syntax.Cls, - S(PlainParamList(vars.toList.map(s => Param(FldFlags.empty, varsMap(s), None)))), + S(PlainParamList(varsList.map(s => Param(FldFlags.empty, varsMap(s), None)))), None, Nil, Nil, Nil, End(), End() ) - (defn, varsMap) + (defn, varsMap, varsList) + + def liftDefnsCls(c: ClsLikeDefn, ctx: LifterCtx): List[Defn] = ??? + private def needsCapture(captureFn: FunDefn, candidate: Defn) = + val candVars = candidate.freeVars + val captureFnVars = getVars(captureFn) + !candVars.intersect(captureFnVars).isEmpty - private def liftFn(f: FunDefn) = + def liftDefnsFn(f: FunDefn, ctx: LifterCtx): List[Defn] = + val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) + val (blk, defns) = f.body.floatOutDefns + // add the mapping from this function's locals to the capture's symbols and the capture path + val captureSym = FlowSymbol("capture") + val captureCtx = ctx + .addLocalPaths(varsMap) + .addCapturePath(f.sym, captureSym.asPath) + + println(captureCtx.capturePaths) + + val thisUsed = ctx.usedLocals(f.sym) + + val bmsCaptures: ListBuffer[(BlockMemberSymbol, List[BlockMemberSymbol])] = ListBuffer.empty + + val newDefns = defns.flatMap: d => + // add parameters for previous defns + val includedCaptures = (f :: captureCtx.prevDefns).collect: + case d if needsCapture(f, d) => (d, VarSymbol(Tree.Ident(d.sym.nme + "$capture"))) + + val extraParams = includedCaptures.map: + case (d, sym) => Param(FldFlags.empty, sym, None) + + bmsCaptures.addOne(d.sym -> includedCaptures.map(_._1.sym)) + + val newCapturePaths = includedCaptures.map: + case (d, sym) => d.sym -> sym.asPath + .toMap + + d match + case d: FunDefn => + val newDef = FunDefn( + f.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body + ) + liftDefnsFn(newDef, captureCtx.addDefn(f).replCapturePaths(newCapturePaths)) + case d: ClsLikeDefn => d :: Nil + // TODO + // liftDefnsCls(d) + case _ => d :: Nil + + val withSymbols = bmsCaptures.map: (bms, captures) => + (bms, captures, VarSymbol(Tree.Ident(bms.nme + "$this"))) + + val bmsPathsMap = withSymbols.map: + case (bms, captures, sym) => bms -> sym.asPath + .toMap + + val newCtx = captureCtx + .addReqdCaptures(bmsCaptures.toMap) + .addBmsPaths(bmsPathsMap) + + // println(f.sym) + + val start = withSymbols.foldRight(blockBuilder): + case ((bms, captures, sym), acc) => + println("CAPTURES:") + println(captures) + println(newCtx.capturePaths) + acc.assign(sym, Call(bms.asPath, captures.map(newCtx.getCapturePath(_).get.asArg))(false)) + + val transformer = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Assign(lhs, rhs, rest) => newCtx.getLocalClosPath(lhs) match + case None => super.applyBlock(b) + case Some(closPath) => + AssignField(closPath, newCtx.getLocalSym(lhs).id, rhs, applyBlock(rest))(N) + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Path = p match + case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match + case None => super.applyPath(p) + case Some(value) => value + + case Value.Ref(l) => + newCtx.getLocalClosPath(l) match + case None => super.applyPath(p) + case Some(closPath) => Select(closPath, newCtx.getLocalSym(l).id)(N) + + case _ => super.applyPath(p) + + if thisUsed.vars.size == 0 then + FunDefn(f.owner, f.sym, f.params, start.rest(transformer.applyBlock(blk))) :: newDefns + else + val paramsSet = f.params.flatMap(_.paramSyms) + val paramsList = varsList.filter(paramsSet.contains(_)) + val bod = blockBuilder + .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList.map(_.asPath))) + .chain(start) + .rest(transformer.applyBlock(blk)) + FunDefn(f.owner, f.sym, f.params, bod) :: captureCls :: newDefns + + // top-level def transform(b: Block) = - given usedMap: UsedLocalsMap = findUsedLocals(b) + val ctx = LifterCtx(findUsedLocals(b), Map.empty, Nil, Map.empty, Map.empty, Map.empty) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - Define(createClosureCls(f)._1, Define(f, applyBlock(rest))) + liftDefnsFn(f, ctx).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(b) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index d16be7905..880fadc30 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -6,49 +6,116 @@ fun f(used1, unused1) = let used2 = unused1 fun g(g_arg) = let used3 = 2 - fun h = used3 - used1 + h + used1 + used2 let unused2 = 2 - class Test() with - fun get() = used2 + used1 - Test() -f(1, 2).get() + g(used2) + unused2 +f(1, 2) //│ JS (unsanitized): -//│ let f1, tmp, f$closure1; -//│ f$closure1 = function f$closure(used10$1, used21$1) { return new f$closure.class(used10$1, used21$1); }; -//│ f$closure1.class = class f$closure { +//│ let f1, g1, f$capture1; +//│ g1 = function g(f$capture2) { +//│ return (g$_arg) => { +//│ let used3; +//│ used3 = 2; +//│ return f$capture2.used10$ + f$capture2.used21$; +//│ }; +//│ }; +//│ f$capture1 = function f$capture(used10$1, used21$1) { return new f$capture.class(used10$1, used21$1); }; +//│ f$capture1.class = class f$capture { //│ constructor(used10$, used21$) { //│ this.used10$ = used10$; //│ this.used21$ = used21$; //│ } -//│ toString() { return "f$closure(" + this.used10$ + ", " + this.used21$ + ")"; } +//│ toString() { return "f$capture(" + this.used10$ + ", " + this.used21$ + ")"; } //│ }; //│ f1 = function f(used1, unused1) { -//│ let Test, g, used2, unused2; -//│ g = function g(g$_arg) { -//│ let h, used3, tmp1; -//│ h = function h() { -//│ return used3; -//│ }; -//│ used3 = 2; -//│ tmp1 = h(); -//│ return used1 + tmp1; -//│ }; -//│ used2 = unused1; +//│ let unused2, tmp, capture, g$this; +//│ capture = new f$capture1(used1); +//│ g$this = g1(capture) ?? null; +//│ capture.used21$ = unused1; //│ unused2 = 2; -//│ Test = function Test() { return new Test.class(); }; -//│ Test.class = class Test { -//│ constructor() {} -//│ get() { -//│ return used2 + used1; -//│ } -//│ toString() { return "Test(" + + ")"; } -//│ }; -//│ return Test(); +//│ tmp = g$this(capture.used21$); +//│ return tmp + unused2; //│ }; -//│ tmp = f1(1, 2); -//│ tmp.get() ?? null -//│ = 3 +//│ f1(1, 2) +//│ = 5 + +:todo +:sjs +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test() with + fun get() = used2 + used1 + Test() +f(1, 2).get() +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: used2 (class hkmc2.semantics.VarSymbol) //│ f$closure1 = function f$closure(used20$1) { return new f$closure.class(used20$1); }; //│ JS (unsanitized): //│ null + +// preserve order +:todo +:sjs +fun f(a1, a2, a3, a4, a5, a6) = + fun g = a1 + a2 + a3 + a4 + a5 + a6 + g +f(1,2,3,4,5,6) +//│ JS (unsanitized): +//│ let f3, g3, f$capture3; +//│ g3 = function g(f$capture4) { +//│ return () => { +//│ let tmp, tmp1, tmp2, tmp3; +//│ tmp = f$capture4.a10$ + f$capture4.a21$; +//│ tmp1 = tmp + f$capture4.a32$; +//│ tmp2 = tmp1 + f$capture4.a43$; +//│ tmp3 = tmp2 + f$capture4.a54$; +//│ return tmp3 + f$capture4.a65$; +//│ }; +//│ }; +//│ f$capture3 = function f$capture(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1) { return new f$capture.class(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1); }; +//│ f$capture3.class = class f$capture { +//│ constructor(a10$, a21$, a32$, a43$, a54$, a65$) { +//│ this.a10$ = a10$; +//│ this.a21$ = a21$; +//│ this.a32$ = a32$; +//│ this.a43$ = a43$; +//│ this.a54$ = a54$; +//│ this.a65$ = a65$; +//│ } +//│ toString() { return "f$capture(" + this.a10$ + ", " + this.a21$ + ", " + this.a32$ + ", " + this.a43$ + ", " + this.a54$ + ", " + this.a65$ + ")"; } +//│ }; +//│ f3 = function f(a1, a2, a3, a4, a5, a6) { +//│ let tmp, capture, g$this; +//│ capture = new f$capture3(a1, a2, a3, a4, a5, a6); +//│ g$this = g3(capture) ?? null; +//│ tmp = g$this(); +//│ return tmp; +//│ }; +//│ f3(1, 2, 3, 4, 5, 6) +//│ = 21 + +:expect '01' +class Tuple(a, b) +fun f() = + let a = 0 + fun g() = set a = 1 + fun h() = a + Tuple(g, h) +let ret = f() +let f1 = ret.a +let f2 = ret.b +let x = f2().toString() +f1() +let y = f2().toString() +x + y +//│ = '01' +//│ f1 = [Function (anonymous)] +//│ f2 = [Function (anonymous)] +//│ ret = Tuple { a: [Function (anonymous)], b: [Function (anonymous)] } +//│ x = '0' +//│ y = '1' + From 93eb9dcc671990b36b1eaeee9a7c112a8ad325d2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 02:12:29 +0800 Subject: [PATCH 007/127] fix typos --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 49 +++++++++---------- .../src/test/mlscript/codegen/Lifter.mls | 34 ++++++++++--- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 8b15e3906..63505db38 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -200,8 +200,6 @@ class Lifter(using State): val captureCtx = ctx .addLocalPaths(varsMap) .addCapturePath(f.sym, captureSym.asPath) - - println(captureCtx.capturePaths) val thisUsed = ctx.usedLocals(f.sym) @@ -210,27 +208,29 @@ class Lifter(using State): val newDefns = defns.flatMap: d => // add parameters for previous defns val includedCaptures = (f :: captureCtx.prevDefns).collect: - case d if needsCapture(f, d) => (d, VarSymbol(Tree.Ident(d.sym.nme + "$capture"))) - - val extraParams = includedCaptures.map: - case (d, sym) => Param(FldFlags.empty, sym, None) - - bmsCaptures.addOne(d.sym -> includedCaptures.map(_._1.sym)) - - val newCapturePaths = includedCaptures.map: - case (d, sym) => d.sym -> sym.asPath - .toMap - - d match - case d: FunDefn => - val newDef = FunDefn( - f.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body - ) - liftDefnsFn(newDef, captureCtx.addDefn(f).replCapturePaths(newCapturePaths)) - case d: ClsLikeDefn => d :: Nil - // TODO - // liftDefnsCls(d) - case _ => d :: Nil + case prev if needsCapture(prev, d) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + + if includedCaptures.isEmpty then d :: Nil + else + val extraParams = includedCaptures.map: + case (d, sym) => Param(FldFlags.empty, sym, None) + + bmsCaptures.addOne(d.sym -> includedCaptures.map(_._1.sym)) + + val newCapturePaths = includedCaptures.map: + case (d, sym) => d.sym -> sym.asPath + .toMap + + d match + case d: FunDefn => + val newDef = FunDefn( + f.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body + ) + liftDefnsFn(newDef, captureCtx.addDefn(f).replCapturePaths(newCapturePaths)) + case d: ClsLikeDefn => d :: Nil + // TODO + // liftDefnsCls(d) + case _ => d :: Nil val withSymbols = bmsCaptures.map: (bms, captures) => (bms, captures, VarSymbol(Tree.Ident(bms.nme + "$this"))) @@ -247,9 +247,6 @@ class Lifter(using State): val start = withSymbols.foldRight(blockBuilder): case ((bms, captures, sym), acc) => - println("CAPTURES:") - println(captures) - println(newCtx.capturePaths) acc.assign(sym, Call(bms.asPath, captures.map(newCtx.getCapturePath(_).get.asArg))(false)) val transformer = new BlockTransformerShallow(SymbolSubst()): diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 880fadc30..19639854f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -39,6 +39,29 @@ f(1, 2) //│ f1(1, 2) //│ = 5 +// don't touch g if not needed +:sjs +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg) = g_arg + 1 + let unused2 = 2 + g(used2) + unused2 +f(1, 2) +//│ JS (unsanitized): +//│ let f3, g3; +//│ g3 = function g(g$_arg) { +//│ return g$_arg + 1; +//│ }; +//│ f3 = function f(used1, unused1) { +//│ let used2, unused2, tmp; +//│ used2 = unused1; +//│ unused2 = 2; +//│ tmp = g3(used2); +//│ return tmp + unused2; +//│ }; +//│ f3(1, 2) +//│ = 5 + :todo :sjs fun f(used1, unused1) = @@ -58,15 +81,14 @@ f(1, 2).get() //│ null // preserve order -:todo :sjs fun f(a1, a2, a3, a4, a5, a6) = fun g = a1 + a2 + a3 + a4 + a5 + a6 g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f3, g3, f$capture3; -//│ g3 = function g(f$capture4) { +//│ let f5, g5, f$capture3; +//│ g5 = function g(f$capture4) { //│ return () => { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = f$capture4.a10$ + f$capture4.a21$; @@ -88,14 +110,14 @@ f(1,2,3,4,5,6) //│ } //│ toString() { return "f$capture(" + this.a10$ + ", " + this.a21$ + ", " + this.a32$ + ", " + this.a43$ + ", " + this.a54$ + ", " + this.a65$ + ")"; } //│ }; -//│ f3 = function f(a1, a2, a3, a4, a5, a6) { +//│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp, capture, g$this; //│ capture = new f$capture3(a1, a2, a3, a4, a5, a6); -//│ g$this = g3(capture) ?? null; +//│ g$this = g5(capture) ?? null; //│ tmp = g$this(); //│ return tmp; //│ }; -//│ f3(1, 2, 3, 4, 5, 6) +//│ f5(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' From f57b038af689d77251988636936e6e354d00c76d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 02:18:11 +0800 Subject: [PATCH 008/127] update test --- hkmc2/shared/src/test/mlscript/codegen/Lifter.mls | 3 --- 1 file changed, 3 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 19639854f..e2f2a9ca5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -76,9 +76,6 @@ fun f(used1, unused1) = Test() f(1, 2).get() //│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: used2 (class hkmc2.semantics.VarSymbol) - //│ f$closure1 = function f$closure(used20$1) { return new f$closure.class(used20$1); }; -//│ JS (unsanitized): -//│ null // preserve order :sjs From 7aa6e0fb27ecc15707230e7f63753117e1c4392b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 03:06:44 +0800 Subject: [PATCH 009/127] fix log and add test --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 29 +++++-- .../src/test/mlscript/codegen/Lifter.mls | 86 +++++++++++++++++++ 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 63505db38..eddc408ee 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -11,6 +11,7 @@ import hkmc2.codegen.llir.FreshInt import scala.collection.mutable.ListBuffer as ListBuffer import scala.collection.mutable.Map as MutMap +import scala.collection.mutable.Set as MutSet // Lifts classes and functions to the top-level. // Assumes the input block does not have any `HandleBlock`s and lamdbas are @@ -23,7 +24,9 @@ class Lifter(using State): case class FreeVars(vars: List[Local], mutated: List[Local]) // use mutable sets locally to avoid reconstructing everything - private case class FreeVarsMut(vars: ListBuffer[Local], mutated: ListBuffer[Local]) + // use the list to maintain the order (for a more readable output when debugging) + // and a list to make sure it's unique + private case class FreeVarsMut(varsSet: MutSet[Local], vars: ListBuffer[Local], mutated: MutSet[Local]) class UsedLocalsMap(mp: Map[BlockMemberSymbol, FreeVars]): def apply(f: BlockMemberSymbol) = mp(f) @@ -97,24 +100,31 @@ class Lifter(using State): // collect all function definitions val retMap: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> f => f -> FreeVarsMut(ListBuffer.empty, ListBuffer.empty) + case _ -> f => f -> FreeVarsMut(MutSet.empty, ListBuffer.empty, MutSet.empty) ) // add this function in case this function has no locals - if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(ListBuffer.empty, ListBuffer.empty)) + if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(MutSet.empty, ListBuffer.empty, MutSet.empty)) // merge recursive call results def merge(next: Map[FunDefn, FreeVarsMut]) = - for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match + for f -> (v @ FreeVarsMut(varsSet, vars, mutated)) <- next do + retMap.get(f) match case None => retMap.addOne(f -> v) case Some(value) => - for l <- vars do retMap(f).vars.addOne(l) - for l <- mutated do retMap(f).mutated.addOne(l) + val freeVars = retMap(f) + for l <- vars if !freeVars.varsSet.contains(l) do + freeVars.varsSet.addOne(l) + freeVars.vars.addOne(l) + for l <- mutated do freeVars.mutated.addOne(l) def addLocal(l: Local, mut: Bool) = lookup.get(l) match case Some(f) => - if mut then retMap(f).mutated.addOne(l) - retMap(f).vars.addOne(l) + val freeVars = retMap(f) + if mut then freeVars.mutated.addOne(l) + if !freeVars.varsSet.contains(l) then + freeVars.varsSet.addOne(l) + freeVars.vars.addOne(l) case None => () val walker = new BlockTransformerShallow(SymbolSubst()): @@ -147,7 +157,7 @@ class Lifter(using State): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => val m = findUsedLocalsImpl(f, Map.empty).map: - case f -> FreeVarsMut(vars, mutated) => + case f -> FreeVarsMut(varsSet, vars, mutated) => f -> FreeVars(vars.toList, mutated.toList) usedMap ++= m super.applyBlock(b) @@ -164,6 +174,7 @@ class Lifter(using State): ) val FreeVars(vars, mutated) = ctx.usedLocals(f.sym) + println(vars) val fresh = FreshInt() diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index e2f2a9ca5..7e20adecf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -118,6 +118,7 @@ f(1,2,3,4,5,6) //│ = 21 :expect '01' +:sjs class Tuple(a, b) fun f() = let a = 0 @@ -131,6 +132,54 @@ let x = f2().toString() f1() let y = f2().toString() x + y +//│ JS (unsanitized): +//│ let f7, Tuple1, g7, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture5; +//│ g7 = function g(f$capture6) { +//│ return () => { +//│ f$capture6.a0$ = 1; +//│ return null; +//│ }; +//│ }; +//│ h1 = function h(f$capture6) { +//│ return () => { +//│ return f$capture6.a0$; +//│ }; +//│ }; +//│ f$capture5 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; +//│ f$capture5.class = class f$capture { +//│ constructor(a0$) { +//│ this.a0$ = a0$; +//│ } +//│ toString() { return "f$capture(" + this.a0$ + ")"; } +//│ }; +//│ f7 = function f() { +//│ let capture, h$this, g$this; +//│ capture = new f$capture5(); +//│ g$this = g7(capture) ?? null; +//│ h$this = h1(capture) ?? null; +//│ capture.a0$ = 0; +//│ return Tuple1(g$this, h$this); +//│ }; +//│ Tuple1 = function Tuple(a1, b1) { return new Tuple.class(a1, b1); }; +//│ Tuple1.class = class Tuple { +//│ constructor(a, b) { +//│ this.a = a; +//│ this.b = b; +//│ } +//│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } +//│ }; +//│ tmp = f7(); +//│ ret = tmp; +//│ f11 = ret.a; +//│ f21 = ret.b; +//│ tmp1 = f21() ?? null; +//│ tmp2 = tmp1.toString() ?? null; +//│ x = tmp2; +//│ tmp3 = f11() ?? null; +//│ tmp4 = f21() ?? null; +//│ tmp5 = tmp4.toString() ?? null; +//│ y = tmp5; +//│ x + y //│ = '01' //│ f1 = [Function (anonymous)] //│ f2 = [Function (anonymous)] @@ -138,3 +187,40 @@ x + y //│ x = '0' //│ y = '1' +class Tuple(a, b) +fun f(used1) = + fun g1(used2) = + fun h() = + set used1 = 10 + set used2 = 100 + fun i() = used1 + used2 + Tuple(h, i) + fun g22() = used1 + Tuple(g1(10), g22) + +:expect '11110110' +let ret = f(1) +let gRet = ret.a +let g2 = ret.b +let hFun = gRet.a +let iFun = gRet.b +let a = iFun() +let b = g2() +hFun() +let c = g2() +let d = iFun() +a.toString() + b + c + d +//│ = '11110110' +//│ a = 11 +//│ b = 1 +//│ c = 10 +//│ d = 110 +//│ g2 = [Function (anonymous)] +//│ gRet = Tuple { a: [Function (anonymous)], b: [Function (anonymous)] } +//│ hFun = [Function (anonymous)] +//│ iFun = [Function (anonymous)] +//│ > Tuple { +//│ > a: Tuple { a: [Function (anonymous)], b: [Function (anonymous)] }, +//│ > b: [Function (anonymous)] +//│ ret = } + From babd7108d9b3b6caef2e693cac069068d8f0dff6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 03:09:31 +0800 Subject: [PATCH 010/127] refactor --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index eddc408ee..b368d4af4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -11,7 +11,7 @@ import hkmc2.codegen.llir.FreshInt import scala.collection.mutable.ListBuffer as ListBuffer import scala.collection.mutable.Map as MutMap -import scala.collection.mutable.Set as MutSet +import scala.collection.mutable.LinkedHashSet // Lifts classes and functions to the top-level. // Assumes the input block does not have any `HandleBlock`s and lamdbas are @@ -24,9 +24,8 @@ class Lifter(using State): case class FreeVars(vars: List[Local], mutated: List[Local]) // use mutable sets locally to avoid reconstructing everything - // use the list to maintain the order (for a more readable output when debugging) - // and a list to make sure it's unique - private case class FreeVarsMut(varsSet: MutSet[Local], vars: ListBuffer[Local], mutated: MutSet[Local]) + // linked hash sets preserve order + private case class FreeVarsMut(vars: LinkedHashSet[Local], mutated: LinkedHashSet[Local]) class UsedLocalsMap(mp: Map[BlockMemberSymbol, FreeVars]): def apply(f: BlockMemberSymbol) = mp(f) @@ -100,31 +99,24 @@ class Lifter(using State): // collect all function definitions val retMap: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> f => f -> FreeVarsMut(MutSet.empty, ListBuffer.empty, MutSet.empty) + case _ -> f => f -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty) ) // add this function in case this function has no locals - if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(MutSet.empty, ListBuffer.empty, MutSet.empty)) + if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty)) // merge recursive call results def merge(next: Map[FunDefn, FreeVarsMut]) = - for f -> (v @ FreeVarsMut(varsSet, vars, mutated)) <- next do - retMap.get(f) match + for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match case None => retMap.addOne(f -> v) case Some(value) => - val freeVars = retMap(f) - for l <- vars if !freeVars.varsSet.contains(l) do - freeVars.varsSet.addOne(l) - freeVars.vars.addOne(l) - for l <- mutated do freeVars.mutated.addOne(l) + for l <- vars do retMap(f).vars.addOne(l) + for l <- mutated do retMap(f).mutated.addOne(l) def addLocal(l: Local, mut: Bool) = lookup.get(l) match case Some(f) => - val freeVars = retMap(f) - if mut then freeVars.mutated.addOne(l) - if !freeVars.varsSet.contains(l) then - freeVars.varsSet.addOne(l) - freeVars.vars.addOne(l) + if mut then retMap(f).mutated.addOne(l) + retMap(f).vars.addOne(l) case None => () val walker = new BlockTransformerShallow(SymbolSubst()): @@ -157,7 +149,7 @@ class Lifter(using State): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => val m = findUsedLocalsImpl(f, Map.empty).map: - case f -> FreeVarsMut(varsSet, vars, mutated) => + case f -> FreeVarsMut(vars, mutated) => f -> FreeVars(vars.toList, mutated.toList) usedMap ++= m super.applyBlock(b) From 19f43d1c435b5dd206e7dfecc16d2d20055cebe3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 15:29:17 +0800 Subject: [PATCH 011/127] Add auxiliary parameters --- .../src/main/scala/hkmc2/codegen/Block.scala | 4 +++- .../hkmc2/codegen/BlockTransformer.scala | 8 +++++-- .../scala/hkmc2/codegen/HandlerLowering.scala | 3 ++- .../src/main/scala/hkmc2/codegen/Lifter.scala | 4 ++-- .../main/scala/hkmc2/codegen/Lowering.scala | 2 +- .../main/scala/hkmc2/codegen/Printer.scala | 5 +++-- .../hkmc2/codegen/StackSafeTransform.scala | 4 ++-- .../scala/hkmc2/codegen/js/JSBuilder.scala | 22 +++++++++++++++++-- .../scala/hkmc2/codegen/llir/Builder.scala | 9 ++++++-- 9 files changed, 46 insertions(+), 15 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index b17ce159c..6cc1ffdc1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -173,7 +173,8 @@ sealed abstract class Defn: lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars - case ClsLikeDefn(own, isym, sym, k, paramsOpt, parentSym, methods, privateFields, publicFields, preCtor, ctor) => + case ClsLikeDefn(own, isym, sym, k, paramsOpt, Nil, parentSym, + methods, privateFields, publicFields, preCtor, ctor) => preCtor.freeVars ++ ctor.freeVars ++ methods.flatMap(_.freeVars) -- privateFields -- publicFields.map(_.sym) @@ -200,6 +201,7 @@ final case class ClsLikeDefn( sym: BlockMemberSymbol, k: syntax.ClsLikeKind, paramsOpt: Opt[ParamList], + auxParams: List[ParamList], parentPath: Opt[Path], methods: Ls[FunDefn], privateFields: Ls[TermSymbol], diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 72c501efc..5679e2a97 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -135,11 +135,13 @@ class BlockTransformer(subst: SymbolSubst): val rhs2 = applyPath(rhs) if (owner2 is owner) && (sym2 is sym) && (rhs2 is rhs) then defn else ValDefn(owner2, k, sym2, rhs2) - case ClsLikeDefn(own, isym, sym, k, paramsOpt, parentPath, methods, privateFields, publicFields, preCtor, ctor) => + case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentPath, methods, + privateFields, publicFields, preCtor, ctor) => val own2 = own.mapConserve(_.subst) val isym2 = isym.subst val sym2 = sym.subst val paramsOpt2 = paramsOpt.mapConserve(applyParamList) + val auxParams2 = auxParams.mapConserve(applyParamList) val parentPath2 = parentPath.mapConserve(applyPath) val methods2 = methods.mapConserve(applyFunDefn) val privateFields2 = privateFields.mapConserve(_.subst) @@ -148,12 +150,14 @@ class BlockTransformer(subst: SymbolSubst): val ctor2 = applyBlock(ctor) if (own2 is own) && (isym2 is isym) && (sym2 is sym) && (paramsOpt2 is paramsOpt) && + (auxParams2 is auxParams) && (parentPath2 is parentPath) && (methods2 is methods) && (privateFields2 is privateFields) && (publicFields2 is publicFields) && (preCtor2 is preCtor) && (ctor2 is ctor) - then defn else ClsLikeDefn(own, isym2, sym2, k, paramsOpt, parentPath2, methods2, privateFields2, publicFields2, preCtor2, ctor2) + then defn else ClsLikeDefn(own2, isym2, sym2, k, paramsOpt2, + auxParams2, parentPath2, methods2, privateFields2, publicFields2, preCtor2, ctor2) def applyArg(arg: Arg): Arg = val val2 = applyPath(arg.value) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index e5c4b2751..22b831628 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -387,7 +387,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): h.cls, BlockMemberSymbol(h.cls.id.name, Nil), syntax.Cls, - N, + N, Nil, S(h.par), handlers, Nil, Nil, Assign(freshTmp(), SimpleCall(Value.Ref(State.builtinOpsMap("super")), Nil), End()), End()) @@ -505,6 +505,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): BlockMemberSymbol(clsSym.nme, Nil), syntax.Cls, S(PlainParamList(Param(FldFlags.empty, pcVar, N) :: Nil)), + Nil, S(contClsPath), resumeFnDef :: Nil, Nil, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index b368d4af4..810876a8b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -181,7 +181,7 @@ class Lifter(using State): None, clsSym, BlockMemberSymbol(nme, Nil), syntax.Cls, S(PlainParamList(varsList.map(s => Param(FldFlags.empty, varsMap(s), None)))), - None, Nil, Nil, Nil, End(), End() + Nil, None, Nil, Nil, Nil, End(), End() ) (defn, varsMap, varsList) @@ -264,7 +264,7 @@ class Lifter(using State): case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match case None => super.applyPath(p) case Some(value) => value - + case Value.Ref(l) => newCtx.getLocalClosPath(l) match case None => super.applyPath(p) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 72ebbdc85..4f5a124d2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -258,7 +258,7 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int], lift: Bool)(using T case s => R(s) val publicFlds = rest2.collect: case td @ TermDefinition(k = (_: syntax.Val)) => td - Define(ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, N, + Define(ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, Nil, N, mtds.flatMap: td => td.body.map: bod => val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 301567567..7d40735ef 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -63,13 +63,14 @@ object Printer: doc"fun ${sym.nme}${docParams} { #{ # ${docBody} #} # }" case ValDefn(owner, k, sym, rhs) => doc"val ${sym.nme} = ${mkDocument(rhs)}" - case ClsLikeDefn(own, _, sym, k, paramsOpt, parentSym, methods, privateFields, publicFields, preCtor, ctor) => + case ClsLikeDefn(own, _, sym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor) => def optFldBody(t: semantics.TermDefinition) = t.body match case Some(x) => doc" = ..." case None => doc"" val clsParams = paramsOpt.fold(Nil)(_.paramSyms) - val ctorParams = clsParams.map(p => summon[Scope].allocateName(p)) + val auxClsParams = auxParams.flatMap(_.paramSyms) + val ctorParams = (clsParams ++ auxClsParams).map(p => summon[Scope].allocateName(p)) val privFields = privateFields.map(x => doc"let ${x.id.name} = ...").mkDocument(sep = doc" # ") val pubFields = publicFields.map(x => doc"${x.k.str} ${x.sym.nme}${optFldBody(x)}").mkDocument(sep = doc" # ") val docPrivFlds = if privateFields.isEmpty then doc"" else doc" # ${privFields}" diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index 27ffe60aa..e10f0d6a5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -134,10 +134,10 @@ class StackSafeTransform(depthLimit: Int)(using State): trivial def rewriteCls(defn: ClsLikeDefn): ClsLikeDefn = - val ClsLikeDefn(owner, isym, sym, k, paramsOpt, + val ClsLikeDefn(owner, isym, sym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor) = defn ClsLikeDefn( - owner, isym, sym, k, paramsOpt, parentPath, methods.map(rewriteFn), privateFields, + owner, isym, sym, k, paramsOpt, auxParams, parentPath, methods.map(rewriteFn), privateFields, publicFields, rewriteBlk(preCtor), rewriteBlk(ctor) ) 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 dc9ed4da0..e15edc428 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -162,9 +162,9 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: Return(Lam(ps, block), false) val (params, bodyDoc) = setupFunction(some(sym.nme), ps, result) doc"${getVar(sym)} = function ${sym.nme}($params) ${ braced(bodyDoc) };" - case ClsLikeDefn(ownr, isym, sym, kind, paramsOpt, par, mtds, privFlds, _pubFlds, preCtor, ctor) => + case ClsLikeDefn(ownr, isym, sym, kind, paramsOpt, auxParams, par, mtds, privFlds, _pubFlds, preCtor, ctor) => // * Note: `_pubFlds` is not used because in JS, fields are not declared - val clsParams = paramsOpt.fold(Nil)(_.paramSyms) + val clsParams = paramsOpt.fold(Nil)(_.paramSyms) ++ auxParams.flatMap(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) val privs = val scp = isym.asInstanceOf[InnerSymbol].privatesScope @@ -217,11 +217,29 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: doc"const $clsTmp = ${clsJS}; ${v} = new ${clsTmp }; # ${v}.class = $clsTmp;" else + val paramsAll = paramsOpt match + case None => auxParams + case Some(value) => value :: auxParams + + val fun = paramsAll match + case ps_ :: pss_ => + val (ps, _) = setupFunction(some(sym.nme), ps_, End()) + val pss = pss_.map(setupFunction(N, _, End())._1) + val paramsDoc = pss.foldLeft(ps): + case (doc, ps) => doc"${doc}, ${ps}" + val bod = doc"return new ${sym.nme}.class($paramsDoc);" + val funBod = pss.foldRight(bod): + case (psDoc, doc) => doc"($psDoc) => ${braced(doc)}" + S(doc"function ${sym.nme}($ps) { $funBod }") + case Nil => N + + /* val fun = paramsOpt match case S(params) => val (ps, bod) = setupFunction(some(sym.nme), params, End()) S(doc"function ${sym.nme}($ps) { return new ${sym.nme}.class($ps); }") case N => N + */ ownr match case S(owner) => val ths = mkThis(owner) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala index ffef9832a..42268459e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -139,9 +139,11 @@ final class LlirBuilder(tl: TraceLogger)(fresh: Fresh, fnUid: FreshInt, clsUid: private def bClsLikeDef(e: ClsLikeDefn)(using ctx: Ctx)(using Raise, Scope): ClassInfo = trace[ClassInfo](s"bClsLikeDef begin", x => s"bClsLikeDef end: ${x.show}"): val ClsLikeDefn( - _own, _isym, sym, kind, paramsOpt, parentSym, methods, privateFields, publicFields, preCtor, ctor) = e + _own, _isym, sym, kind, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor) = e if !ctx.is_top_level then errStop(msg"Non top-level definition ${sym.nme} not supported") + else if !auxParams.isEmpty then + errStop(msg"The class ${sym.nme} has auxiliary parameters, which are not yet supported") else val clsDefn = sym.defn.getOrElse(die) val clsParams = paramsOpt.fold(Nil)(_.paramSyms) @@ -300,7 +302,10 @@ final class LlirBuilder(tl: TraceLogger)(fresh: Fresh, fnUid: FreshInt, clsUid: def registerClasses(b: Block)(using ctx: Ctx)(using Raise, Scope): Ctx = b match - case Define(cd @ ClsLikeDefn(_own, isym, sym, kind, _paramsOpt, parentSym, methods, privateFields, publicFields, preCtor, ctor), rest) => + case Define(cd @ ClsLikeDefn(_own, isym, sym, kind, _paramsOpt, auxParams, + parentSym, methods, privateFields, publicFields, preCtor, ctor), rest) => + if !auxParams.isEmpty then + errStop(msg"The class ${sym.nme} has auxiliary parameters, which are not yet supported") val c = bClsLikeDef(cd) ctx.class_acc += c val new_ctx = ctx.addClassName(sym, Name(c.name)).addClassName(isym, Name(c.name)) From 7a3ae4ee01f84e7f6e5b37a9243a8f30d3f92d02 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 15:54:29 +0800 Subject: [PATCH 012/127] small refactor --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 810876a8b..3b8498ba5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -44,7 +44,7 @@ class Lifter(using State): ) - class LifterCtx( + case class LifterCtx( val usedLocals: UsedLocalsMap, val localSyms: Map[Local, VarSymbol], val prevDefns: List[FunDefn], @@ -62,20 +62,17 @@ class Lifter(using State): def getLocalSym(l: Local) = localSyms(l) // the path to a local value containing this function with the captures already applied def getBmsPath(b: BlockMemberSymbol) = bmsPaths.get(b) - - def addDefn(f: FunDefn) = - LifterCtx(usedLocals, localSyms, f :: prevDefns, capturePaths, bmsReqdCaptures, bmsPaths) - def addLocalPaths(m: Map[Local, VarSymbol]) = - LifterCtx(usedLocals, localSyms ++ m, prevDefns, capturePaths, bmsReqdCaptures, bmsPaths) - def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = - LifterCtx(usedLocals, localSyms, prevDefns, paths, bmsReqdCaptures, bmsPaths) - def addCapturePath(src: BlockMemberSymbol, path: Path) = - LifterCtx(usedLocals, localSyms, prevDefns, capturePaths + (src -> path), bmsReqdCaptures, bmsPaths) - def addReqdCaptures(mp: Map[BlockMemberSymbol, List[BlockMemberSymbol]]) = - LifterCtx(usedLocals, localSyms, prevDefns, capturePaths, bmsReqdCaptures ++ mp, bmsPaths) - def addBmsPaths(paths: Map[BlockMemberSymbol, Path]) = - LifterCtx(usedLocals, localSyms, prevDefns, capturePaths, bmsReqdCaptures, bmsPaths ++ paths) - + + def addDefn(f: FunDefn) = copy(prevDefns = f :: prevDefns) + def addLocalPaths(m: Map[Local, VarSymbol]) = copy(localSyms = localSyms ++ m) + def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = copy(capturePaths = paths) + def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) + def addReqdCaptures(mp: Map[BlockMemberSymbol, List[BlockMemberSymbol]]) = copy(bmsReqdCaptures = bmsReqdCaptures ++ mp) + def addBmsPaths(paths: Map[BlockMemberSymbol, Path]) = copy(bmsPaths = bmsPaths ++ paths) + + object LifterCtx: + def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Map.empty, Map.empty, Map.empty) + def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) def getVars(f: FunDefn): Set[Local] = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: case s: FlowSymbol => s @@ -204,10 +201,7 @@ class Lifter(using State): .addLocalPaths(varsMap) .addCapturePath(f.sym, captureSym.asPath) - val thisUsed = ctx.usedLocals(f.sym) - val bmsCaptures: ListBuffer[(BlockMemberSymbol, List[BlockMemberSymbol])] = ListBuffer.empty - val newDefns = defns.flatMap: d => // add parameters for previous defns val includedCaptures = (f :: captureCtx.prevDefns).collect: @@ -235,8 +229,10 @@ class Lifter(using State): // liftDefnsCls(d) case _ => d :: Nil + // creates the triple: + // (bms, that bms's required captures, the symbol to that bms) val withSymbols = bmsCaptures.map: (bms, captures) => - (bms, captures, VarSymbol(Tree.Ident(bms.nme + "$this"))) + (bms, captures, FlowSymbol(bms.nme + "$this")) val bmsPathsMap = withSymbols.map: case (bms, captures, sym) => bms -> sym.asPath @@ -264,15 +260,13 @@ class Lifter(using State): case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match case None => super.applyPath(p) case Some(value) => value - case Value.Ref(l) => newCtx.getLocalClosPath(l) match case None => super.applyPath(p) case Some(closPath) => Select(closPath, newCtx.getLocalSym(l).id)(N) - case _ => super.applyPath(p) - if thisUsed.vars.size == 0 then + if ctx.usedLocals(f.sym).vars.size == 0 then FunDefn(f.owner, f.sym, f.params, start.rest(transformer.applyBlock(blk))) :: newDefns else val paramsSet = f.params.flatMap(_.paramSyms) @@ -286,7 +280,7 @@ class Lifter(using State): // top-level def transform(b: Block) = - val ctx = LifterCtx(findUsedLocals(b), Map.empty, Nil, Map.empty, Map.empty, Map.empty) + val ctx = LifterCtx.withLocals(findUsedLocals(b)) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match From 4e2f4a358a2add55ada44a8312c054714c3ea121 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 18:28:59 +0800 Subject: [PATCH 013/127] capture at BlockMemberSymbol refs instead of at the start of the function --- .../hkmc2/codegen/BlockTransformer.scala | 16 ++ .../src/main/scala/hkmc2/codegen/Lifter.scala | 142 ++++++++++++------ .../src/test/mlscript/codegen/Lifter.mls | 8 +- 3 files changed, 119 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 5679e2a97..1f7025e75 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -225,3 +225,19 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst then b else HandleBlock(l2, res2, par2, cls2, hdr2, bod, rst2) case _ => super.applyBlock(b) +// does not traverse into any other block +class BlockTransformerNoRec(subst: SymbolSubst) extends BlockTransformerShallow(subst): + override def applyBlock(b: Block): Block = b match + case Match(scrut, arms, dflt, rest) => + val scrut2 = applyPath(scrut) + if (scrut is scrut2) then b else Match(scrut2, arms, dflt, rest) + case Assign(lhs, rhs, rest) => + val lhs2 = lhs.subst + val rhs2 = applyResult(rhs) + if (lhs is lhs2) && (rhs is rhs2) then b else Assign(lhs2, rhs2, rest) + case AssignField(lhs, ident, rhs, rest) => + val lhs2 = applyPath(lhs) + val rhs2 = applyResult(rhs) + if rhs is rhs2 then b else AssignField(lhs2, ident, rhs2, rest)(N) + case _: Label | _: Begin | _: TryBlock | _: Define | _: HandleBlock | _: End => b + case _: Return | _: Break | _: Continue | _: HandleBlockReturn | _: Throw => super.applyBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 3b8498ba5..8ba8c4380 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -9,9 +9,11 @@ import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt -import scala.collection.mutable.ListBuffer as ListBuffer -import scala.collection.mutable.Map as MutMap +import scala.collection.mutable.ListBuffer import scala.collection.mutable.LinkedHashSet +import scala.collection.mutable.LinkedHashMap +import scala.collection.mutable.Map as MutMap +import scala.annotation.nowarn // Lifts classes and functions to the top-level. // Assumes the input block does not have any `HandleBlock`s and lamdbas are @@ -65,6 +67,7 @@ class Lifter(using State): def addDefn(f: FunDefn) = copy(prevDefns = f :: prevDefns) def addLocalPaths(m: Map[Local, VarSymbol]) = copy(localSyms = localSyms ++ m) + def getReqdCapture(sym: BlockMemberSymbol) = bmsReqdCaptures.get(sym) def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = copy(capturePaths = paths) def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addReqdCaptures(mp: Map[BlockMemberSymbol, List[BlockMemberSymbol]]) = copy(bmsReqdCaptures = bmsReqdCaptures ++ mp) @@ -163,7 +166,6 @@ class Lifter(using State): ) val FreeVars(vars, mutated) = ctx.usedLocals(f.sym) - println(vars) val fresh = FreshInt() @@ -190,47 +192,70 @@ class Lifter(using State): val captureFnVars = getVars(captureFn) !candVars.intersect(captureFnVars).isEmpty - def liftDefnsFn(f: FunDefn, ctx: LifterCtx): List[Defn] = + case class LiftedInfo( + val extraDefns: List[Defn], + val reqdCaptures: List[BlockMemberSymbol] + ) + case class Lifted( + val liftedDefn: Defn, + val info: Opt[LiftedInfo] + ): + def withInfo = LiftedInfo.apply.tupled andThen (info => Lifted(liftedDefn, S(info))) + + object Lifted: + def of(d: Defn) = Lifted(d, N) + + inline def liftOutDefn(base: FunDefn, d: Defn, ctx: LifterCtx): Lifted = + @nowarn("msg=New anonymous class definition will be duplicated at each inline site") // inlined only at one place + val includedCaptures = ctx.prevDefns.collect: + case prev if needsCapture(prev, d) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + + if includedCaptures.isEmpty then Lifted.of(d) + else + val extraParams = includedCaptures.map: + case (d, sym) => Param(FldFlags.empty, sym, None) + + val newCapturePaths = includedCaptures.map: + case (d, sym) => d.sym -> sym.asPath + .toMap + + d match + case d: FunDefn => + val newDef = FunDefn( + base.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body + ) + val (lifted, extra) = liftDefnsInFn(newDef, ctx.replCapturePaths(newCapturePaths)) + Lifted.of(lifted).withInfo(extra, includedCaptures.map(_._1.sym)) + case d: ClsLikeDefn => Lifted.of(d) + // TODO + // liftDefnsCls(d) + case _ => Lifted.of(d) + + def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = + Call(sym.asPath, ctx.getReqdCapture(sym).get.map(ctx.getCapturePath(_).get.asArg))(false) + + def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): (Defn, List[Defn]) = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) - val (blk, defns) = f.body.floatOutDefns + val (blk, nested) = f.body.floatOutDefns // add the mapping from this function's locals to the capture's symbols and the capture path val captureSym = FlowSymbol("capture") val captureCtx = ctx - .addLocalPaths(varsMap) - .addCapturePath(f.sym, captureSym.asPath) - - val bmsCaptures: ListBuffer[(BlockMemberSymbol, List[BlockMemberSymbol])] = ListBuffer.empty - val newDefns = defns.flatMap: d => - // add parameters for previous defns - val includedCaptures = (f :: captureCtx.prevDefns).collect: - case prev if needsCapture(prev, d) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + .addLocalPaths(varsMap) // how to access locals via. the capture class from now on + .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture + val nestedCtx = captureCtx.addDefn(f) - if includedCaptures.isEmpty then d :: Nil - else - val extraParams = includedCaptures.map: - case (d, sym) => Param(FldFlags.empty, sym, None) - - bmsCaptures.addOne(d.sym -> includedCaptures.map(_._1.sym)) - - val newCapturePaths = includedCaptures.map: - case (d, sym) => d.sym -> sym.asPath - .toMap - - d match - case d: FunDefn => - val newDef = FunDefn( - f.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body - ) - liftDefnsFn(newDef, captureCtx.addDefn(f).replCapturePaths(newCapturePaths)) - case d: ClsLikeDefn => d :: Nil - // TODO - // liftDefnsCls(d) - case _ => d :: Nil + val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) + val bmsCaptures = nestedLifted.collect: + case Lifted(liftedDefn, S(LiftedInfo(extraDefns, reqdCaptures))) => liftedDefn.sym -> reqdCaptures + .toMap + val newDefns = nestedLifted.flatMap: + case Lifted(liftedDefn, S(info)) => liftedDefn :: info.extraDefns + case Lifted(liftedDefn, N) => liftedDefn :: Nil // creates the triple: - // (bms, that bms's required captures, the symbol to that bms) + // (bms, that bms's required captures, the symbol to that bms with captures applied) val withSymbols = bmsCaptures.map: (bms, captures) => (bms, captures, FlowSymbol(bms.nme + "$this")) @@ -242,13 +267,32 @@ class Lifter(using State): .addReqdCaptures(bmsCaptures.toMap) .addBmsPaths(bmsPathsMap) - // println(f.sym) - + val start = withSymbols.foldRight(blockBuilder): case ((bms, captures, sym), acc) => acc.assign(sym, Call(bms.asPath, captures.map(newCtx.getCapturePath(_).get.asArg))(false)) + + // replaces references to BlockMemberSymbols as needed with fresh variables, and + // returns the mapping from the symbol to the required variable + def rewriteBms(b: Block, ctx: LifterCtx) = + val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty + + val walker = new BlockTransformerNoRec(SymbolSubst()): + // only scan within the block. don't traverse + override def applyValue(v: Value): Value = v match + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdCaptures.contains(l) => + val newSym = syms.get(l) match + case None => + val newSym = FlowSymbol(l.nme + "$this") + syms.addOne(l -> newSym) + newSym + case Some(value) => value + Value.Ref(newSym) + case _ => super.applyValue(v) + (walker.applyBlock(b), syms.toList) + - val transformer = new BlockTransformerShallow(SymbolSubst()): + val transformer1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Assign(lhs, rhs, rest) => newCtx.getLocalClosPath(lhs) match case None => super.applyBlock(b) @@ -257,25 +301,36 @@ class Lifter(using State): case _ => super.applyBlock(b) override def applyPath(p: Path): Path = p match + /* case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match case None => super.applyPath(p) case Some(value) => value + */ case Value.Ref(l) => newCtx.getLocalClosPath(l) match case None => super.applyPath(p) case Some(closPath) => Select(closPath, newCtx.getLocalSym(l).id)(N) case _ => super.applyPath(p) + + val transformer2 = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = + val (rewriten, syms) = rewriteBms(b, newCtx) + val pre = syms.foldLeft(blockBuilder): + case (blk, (bms, local)) => + blk.assign(local, createCall(bms, newCtx)) + pre.rest(super.applyBlock(rewriten)) + + val transformed = blk |> transformer1.applyBlock |> transformer2.applyBlock if ctx.usedLocals(f.sym).vars.size == 0 then - FunDefn(f.owner, f.sym, f.params, start.rest(transformer.applyBlock(blk))) :: newDefns + (FunDefn(f.owner, f.sym, f.params, transformed), newDefns) else val paramsSet = f.params.flatMap(_.paramSyms) val paramsList = varsList.filter(paramsSet.contains(_)) val bod = blockBuilder .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList.map(_.asPath))) - .chain(start) - .rest(transformer.applyBlock(blk)) - FunDefn(f.owner, f.sym, f.params, bod) :: captureCls :: newDefns + .rest(transformed) + (FunDefn(f.owner, f.sym, f.params, bod), captureCls :: newDefns) // top-level @@ -285,6 +340,7 @@ class Lifter(using State): val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - liftDefnsFn(f, ctx).foldLeft(rest)((acc, defn) => Define(defn, acc)) + val (d, extra) = liftDefnsInFn(f, ctx) + (d :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(b) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 7e20adecf..f3da551af 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -30,9 +30,9 @@ f(1, 2) //│ f1 = function f(used1, unused1) { //│ let unused2, tmp, capture, g$this; //│ capture = new f$capture1(used1); -//│ g$this = g1(capture) ?? null; //│ capture.used21$ = unused1; //│ unused2 = 2; +//│ g$this = g1(capture) ?? null; //│ tmp = g$this(capture.used21$); //│ return tmp + unused2; //│ }; @@ -124,7 +124,7 @@ fun f() = let a = 0 fun g() = set a = 1 fun h() = a - Tuple(g, h) + Tuple(g, h) let ret = f() let f1 = ret.a let f2 = ret.b @@ -153,11 +153,11 @@ x + y //│ toString() { return "f$capture(" + this.a0$ + ")"; } //│ }; //│ f7 = function f() { -//│ let capture, h$this, g$this; +//│ let capture, g$this, h$this; //│ capture = new f$capture5(); +//│ capture.a0$ = 0; //│ g$this = g7(capture) ?? null; //│ h$this = h1(capture) ?? null; -//│ capture.a0$ = 0; //│ return Tuple1(g$this, h$this); //│ }; //│ Tuple1 = function Tuple(a1, b1) { return new Tuple.class(a1, b1); }; From 41bf24b9e8d4b7c78520c2a9f3af7cf69cefff97 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 18:54:31 +0800 Subject: [PATCH 014/127] small optimization --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 15 ++- .../src/test/mlscript/codegen/Lifter.mls | 97 +++++++++++++------ 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 8ba8c4380..19998c92d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -43,9 +43,8 @@ class Lifter(using State): def from(mp: Map[FunDefn, FreeVars]) = UsedLocalsMap(mp.map: case a -> b => a.sym -> b - ) - - + ) + case class LifterCtx( val usedLocals: UsedLocalsMap, val localSyms: Map[Local, VarSymbol], @@ -273,12 +272,20 @@ class Lifter(using State): acc.assign(sym, Call(bms.asPath, captures.map(newCtx.getCapturePath(_).get.asArg))(false)) // replaces references to BlockMemberSymbols as needed with fresh variables, and - // returns the mapping from the symbol to the required variable + // returns the mapping from the symbol to the required variable. When possible, + // it also directly rewrites Results. def rewriteBms(b: Block, ctx: LifterCtx) = val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty val walker = new BlockTransformerNoRec(SymbolSubst()): // only scan within the block. don't traverse + + // if possible, directly create the call and replace the result with it + override def applyResult(r: Result): Result = r match + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdCaptures.contains(l) => createCall(l, ctx) + case _ => super.applyResult(r) + + // otherwise, there's no choice but to create the call earlier override def applyValue(v: Value): Value = v match case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdCaptures.contains(l) => val newSym = syms.get(l) match diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index f3da551af..5baf7d687 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -39,6 +39,45 @@ f(1, 2) //│ f1(1, 2) //│ = 5 +:sjs +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg) = + let used3 = 2 + used1 + used2 + let unused2 = 2 + let foo = g + foo(used2) + unused2 +f(1, 2) +//│ JS (unsanitized): +//│ let f3, g3, f$capture3; +//│ g3 = function g(f$capture4) { +//│ return (g$_arg) => { +//│ let used3; +//│ used3 = 2; +//│ return f$capture4.used10$ + f$capture4.used21$; +//│ }; +//│ }; +//│ f$capture3 = function f$capture(used10$1, used21$1) { return new f$capture.class(used10$1, used21$1); }; +//│ f$capture3.class = class f$capture { +//│ constructor(used10$, used21$) { +//│ this.used10$ = used10$; +//│ this.used21$ = used21$; +//│ } +//│ toString() { return "f$capture(" + this.used10$ + ", " + this.used21$ + ")"; } +//│ }; +//│ f3 = function f(used1, unused1) { +//│ let unused2, foo, tmp, capture; +//│ capture = new f$capture3(used1); +//│ capture.used21$ = unused1; +//│ unused2 = 2; +//│ foo = g3(capture) ?? null; +//│ tmp = foo(capture.used21$) ?? null; +//│ return tmp + unused2; +//│ }; +//│ f3(1, 2) +//│ = 5 + // don't touch g if not needed :sjs fun f(used1, unused1) = @@ -48,18 +87,18 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f3, g3; -//│ g3 = function g(g$_arg) { +//│ let f5, g5; +//│ g5 = function g(g$_arg) { //│ return g$_arg + 1; //│ }; -//│ f3 = function f(used1, unused1) { +//│ f5 = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g3(used2); +//│ tmp = g5(used2); //│ return tmp + unused2; //│ }; -//│ f3(1, 2) +//│ f5(1, 2) //│ = 5 :todo @@ -84,19 +123,19 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f5, g5, f$capture3; -//│ g5 = function g(f$capture4) { +//│ let f7, g7, f$capture5; +//│ g7 = function g(f$capture6) { //│ return () => { //│ let tmp, tmp1, tmp2, tmp3; -//│ tmp = f$capture4.a10$ + f$capture4.a21$; -//│ tmp1 = tmp + f$capture4.a32$; -//│ tmp2 = tmp1 + f$capture4.a43$; -//│ tmp3 = tmp2 + f$capture4.a54$; -//│ return tmp3 + f$capture4.a65$; +//│ tmp = f$capture6.a10$ + f$capture6.a21$; +//│ tmp1 = tmp + f$capture6.a32$; +//│ tmp2 = tmp1 + f$capture6.a43$; +//│ tmp3 = tmp2 + f$capture6.a54$; +//│ return tmp3 + f$capture6.a65$; //│ }; //│ }; -//│ f$capture3 = function f$capture(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1) { return new f$capture.class(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1); }; -//│ f$capture3.class = class f$capture { +//│ f$capture5 = function f$capture(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1) { return new f$capture.class(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1); }; +//│ f$capture5.class = class f$capture { //│ constructor(a10$, a21$, a32$, a43$, a54$, a65$) { //│ this.a10$ = a10$; //│ this.a21$ = a21$; @@ -107,14 +146,14 @@ f(1,2,3,4,5,6) //│ } //│ toString() { return "f$capture(" + this.a10$ + ", " + this.a21$ + ", " + this.a32$ + ", " + this.a43$ + ", " + this.a54$ + ", " + this.a65$ + ")"; } //│ }; -//│ f5 = function f(a1, a2, a3, a4, a5, a6) { +//│ f7 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp, capture, g$this; -//│ capture = new f$capture3(a1, a2, a3, a4, a5, a6); -//│ g$this = g5(capture) ?? null; +//│ capture = new f$capture5(a1, a2, a3, a4, a5, a6); +//│ g$this = g7(capture) ?? null; //│ tmp = g$this(); //│ return tmp; //│ }; -//│ f5(1, 2, 3, 4, 5, 6) +//│ f7(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' @@ -133,30 +172,30 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f7, Tuple1, g7, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture5; -//│ g7 = function g(f$capture6) { +//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture7; +//│ g9 = function g(f$capture8) { //│ return () => { -//│ f$capture6.a0$ = 1; +//│ f$capture8.a0$ = 1; //│ return null; //│ }; //│ }; -//│ h1 = function h(f$capture6) { +//│ h1 = function h(f$capture8) { //│ return () => { -//│ return f$capture6.a0$; +//│ return f$capture8.a0$; //│ }; //│ }; -//│ f$capture5 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; -//│ f$capture5.class = class f$capture { +//│ f$capture7 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; +//│ f$capture7.class = class f$capture { //│ constructor(a0$) { //│ this.a0$ = a0$; //│ } //│ toString() { return "f$capture(" + this.a0$ + ")"; } //│ }; -//│ f7 = function f() { +//│ f9 = function f() { //│ let capture, g$this, h$this; -//│ capture = new f$capture5(); +//│ capture = new f$capture7(); //│ capture.a0$ = 0; -//│ g$this = g7(capture) ?? null; +//│ g$this = g9(capture) ?? null; //│ h$this = h1(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; @@ -168,7 +207,7 @@ x + y //│ } //│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } //│ }; -//│ tmp = f7(); +//│ tmp = f9(); //│ ret = tmp; //│ f11 = ret.a; //│ f21 = ret.b; From d7c7beffd642cad05d6620602cf529493f832d06 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 23:08:26 +0800 Subject: [PATCH 015/127] pass immutable variables as args --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 143 +++++++++++----- .../src/test/mlscript/codegen/Lifter.mls | 162 +++++++++++------- 2 files changed, 196 insertions(+), 109 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 19998c92d..3444592a4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -45,13 +45,23 @@ class Lifter(using State): case a -> b => a.sym -> b ) + // usedLocals: describes the locals belonging to each function that are accessed/mutated by nested defns + // localCaptureSyms: the symbols in a capture corresponding to a particular local + // prevFnDefns: fun defns that have already been traversed + // prevClsDefns: class defns that have already been traversed + // capturePaths: the path to access a particular function's capture in the local scope + // bmsReqdInfo: the (mutable) captures and (immutable) local variables each function requires + // bmsPaths: the path to access a particular BlockMemberSymbol in the local scope w/ the captures already applied + // localPaths: the path to access a particular local (possibly belonging to a prev fn/class) in the current scope case class LifterCtx( val usedLocals: UsedLocalsMap, - val localSyms: Map[Local, VarSymbol], - val prevDefns: List[FunDefn], + val localCaptureSyms: Map[Local, VarSymbol], + val prevFnDefns: List[FunDefn], + val prevClsDefns: List[ClsLikeDefn], val capturePaths: Map[BlockMemberSymbol, Path], - val bmsReqdCaptures: Map[BlockMemberSymbol, List[BlockMemberSymbol]], // required captures + val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures val bmsPaths: Map[BlockMemberSymbol, Path], + val localPaths: Map[Local, Local] ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -60,21 +70,26 @@ class Lifter(using State): // the path to access the capture of the function that a local belongs to def getLocalClosPath(l: Local) = lookup(l).flatMap(capturePaths.get(_)) // the symbol in the capture corresponding to a particular local - def getLocalSym(l: Local) = localSyms(l) + def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) // the path to a local value containing this function with the captures already applied def getBmsPath(b: BlockMemberSymbol) = bmsPaths.get(b) + // how to access a variable in the local scope + def getLocalPath(l: Local) = localPaths.get(l) - def addDefn(f: FunDefn) = copy(prevDefns = f :: prevDefns) - def addLocalPaths(m: Map[Local, VarSymbol]) = copy(localSyms = localSyms ++ m) - def getReqdCapture(sym: BlockMemberSymbol) = bmsReqdCaptures.get(sym) + def addDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) + def addLocalCaptureSyms(m: Map[Local, VarSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) + def getBmsReqdInfo(sym: BlockMemberSymbol) = bmsReqdInfo.get(sym) def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = copy(capturePaths = paths) def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) - def addReqdCaptures(mp: Map[BlockMemberSymbol, List[BlockMemberSymbol]]) = copy(bmsReqdCaptures = bmsReqdCaptures ++ mp) + def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) def addBmsPaths(paths: Map[BlockMemberSymbol, Path]) = copy(bmsPaths = bmsPaths ++ paths) + def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) + def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Map.empty, Map.empty, Map.empty) + def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) + def getVars(f: FunDefn): Set[Local] = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: case s: FlowSymbol => s @@ -168,12 +183,12 @@ class Lifter(using State): val fresh = FreshInt() - val varsMap: Map[Local, VarSymbol] = vars.map: s => + val varsMap: Map[Local, VarSymbol] = mutated.map: s => val id = fresh.make s -> VarSymbol(Tree.Ident(s.nme + id + "$")) .toMap - val varsList = vars.toList + val varsList = mutated.toList val defn = ClsLikeDefn( None, clsSym, BlockMemberSymbol(nme, Nil), @@ -186,52 +201,76 @@ class Lifter(using State): def liftDefnsCls(c: ClsLikeDefn, ctx: LifterCtx): List[Defn] = ??? - private def needsCapture(captureFn: FunDefn, candidate: Defn) = + private def needsCapture(captureFn: FunDefn, candidate: Defn, ctx: LifterCtx) = val candVars = candidate.freeVars - val captureFnVars = getVars(captureFn) + val captureFnVars = ctx.usedLocals(captureFn.sym).mutated.toSet !candVars.intersect(captureFnVars).isEmpty + + private def neededImutLocals(captureFn: FunDefn, candidate: Defn, ctx: LifterCtx) = + val candVars = candidate.freeVars + val captureFnVars = ctx.usedLocals(captureFn.sym) + val mutVars = captureFnVars.mutated.toSet + val imutVars = captureFnVars.vars + imutVars.filter: s => + !mutVars.contains(s) && candVars.contains(s) case class LiftedInfo( - val extraDefns: List[Defn], - val reqdCaptures: List[BlockMemberSymbol] + val reqdCaptures: List[BlockMemberSymbol], + val reqdVars: List[Local] ) case class Lifted( val liftedDefn: Defn, + val extraDefns: List[Defn], val info: Opt[LiftedInfo] ): - def withInfo = LiftedInfo.apply.tupled andThen (info => Lifted(liftedDefn, S(info))) + def withInfo = LiftedInfo.apply.tupled andThen (info => Lifted(liftedDefn, extraDefns, S(info))) object Lifted: - def of(d: Defn) = Lifted(d, N) + def of(d: Defn, ed: List[Defn]) = Lifted(d, ed, N) inline def liftOutDefn(base: FunDefn, d: Defn, ctx: LifterCtx): Lifted = @nowarn("msg=New anonymous class definition will be duplicated at each inline site") // inlined only at one place - val includedCaptures = ctx.prevDefns.collect: - case prev if needsCapture(prev, d) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + val includedCaptures = ctx.prevFnDefns.collect: + case prev if needsCapture(prev, d, ctx) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + + val includedLocals = ctx.prevFnDefns.flatMap: ls => + neededImutLocals(ls, d, ctx).map: l => + (l, VarSymbol(Tree.Ident(l.nme))) - if includedCaptures.isEmpty then Lifted.of(d) + if includedCaptures.isEmpty && includedLocals.isEmpty then Lifted.of(d, Nil) else - val extraParams = includedCaptures.map: + val extraParamsCaptures = includedCaptures.map: case (d, sym) => Param(FldFlags.empty, sym, None) - val newCapturePaths = includedCaptures.map: case (d, sym) => d.sym -> sym.asPath .toMap + val extraParamsLocals = includedLocals.map: + case (d, sym) => Param(FldFlags.empty, sym, None) + val newLocalsPaths = includedLocals.map: + case (d, sym) => d -> sym + .toMap + + val extraParams = extraParamsLocals ++ extraParamsCaptures + d match case d: FunDefn => val newDef = FunDefn( base.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body ) - val (lifted, extra) = liftDefnsInFn(newDef, ctx.replCapturePaths(newCapturePaths)) - Lifted.of(lifted).withInfo(extra, includedCaptures.map(_._1.sym)) - case d: ClsLikeDefn => Lifted.of(d) + val newCtx = ctx.replCapturePaths(newCapturePaths).replLocalPaths(newLocalsPaths) + val (lifted, extra) = liftDefnsInFn(newDef, newCtx) + Lifted.of(lifted, extra).withInfo(includedCaptures.map(_._1.sym), includedLocals.map(_._1)) + case d: ClsLikeDefn => Lifted.of(d, Nil) // TODO // liftDefnsCls(d) - case _ => Lifted.of(d) + case _ => Lifted.of(d, Nil) def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = - Call(sym.asPath, ctx.getReqdCapture(sym).get.map(ctx.getCapturePath(_).get.asArg))(false) + val info = ctx.getBmsReqdInfo(sym).get + val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) + val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) + Call(sym.asPath, localsArgs ++ capturesArgs)(false) def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): (Defn, List[Defn]) = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) @@ -241,35 +280,38 @@ class Lifter(using State): // add the mapping from this function's locals to the capture's symbols and the capture path val captureSym = FlowSymbol("capture") val captureCtx = ctx - .addLocalPaths(varsMap) // how to access locals via. the capture class from now on + .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture val nestedCtx = captureCtx.addDefn(f) val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) - val bmsCaptures = nestedLifted.collect: - case Lifted(liftedDefn, S(LiftedInfo(extraDefns, reqdCaptures))) => liftedDefn.sym -> reqdCaptures + val bmsInfo = nestedLifted.collect: + case Lifted(liftedDefn, extraDefns, S(info)) => + liftedDefn.sym -> info .toMap val newDefns = nestedLifted.flatMap: - case Lifted(liftedDefn, S(info)) => liftedDefn :: info.extraDefns - case Lifted(liftedDefn, N) => liftedDefn :: Nil + case Lifted(liftedDefn, extraDefns, _) => liftedDefn :: extraDefns // creates the triple: // (bms, that bms's required captures, the symbol to that bms with captures applied) - val withSymbols = bmsCaptures.map: (bms, captures) => + val withSymbols = bmsInfo.map: (bms, captures) => (bms, captures, FlowSymbol(bms.nme + "$this")) val bmsPathsMap = withSymbols.map: case (bms, captures, sym) => bms -> sym.asPath .toMap + val thisVars = ctx.usedLocals(f.sym) + val newCtx = captureCtx - .addReqdCaptures(bmsCaptures.toMap) + .addBmsReqdInfo(bmsInfo.toMap) .addBmsPaths(bmsPathsMap) + .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) val start = withSymbols.foldRight(blockBuilder): case ((bms, captures, sym), acc) => - acc.assign(sym, Call(bms.asPath, captures.map(newCtx.getCapturePath(_).get.asArg))(false)) + acc.assign(sym, Call(bms.asPath, captures.reqdCaptures.map(newCtx.getCapturePath(_).get.asArg))(false)) // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, @@ -282,12 +324,12 @@ class Lifter(using State): // if possible, directly create the call and replace the result with it override def applyResult(r: Result): Result = r match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdCaptures.contains(l) => createCall(l, ctx) + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, newCtx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdCaptures.contains(l) => + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => val newSym = syms.get(l) match case None => val newSym = FlowSymbol(l.nme + "$this") @@ -297,14 +339,18 @@ class Lifter(using State): Value.Ref(newSym) case _ => super.applyValue(v) (walker.applyBlock(b), syms.toList) + end rewriteBms + // rewrites references to variables val transformer1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match - case Assign(lhs, rhs, rest) => newCtx.getLocalClosPath(lhs) match - case None => super.applyBlock(b) - case Some(closPath) => - AssignField(closPath, newCtx.getLocalSym(lhs).id, rhs, applyBlock(rest))(N) + case Assign(lhs, rhs, rest) => newCtx.getLocalCaptureSym(lhs) match + case Some(captureSym) => + AssignField(newCtx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) + case None => newCtx.getLocalPath(lhs) match + case None => super.applyBlock(b) + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) case _ => super.applyBlock(b) override def applyPath(p: Path): Path = p match @@ -313,12 +359,14 @@ class Lifter(using State): case None => super.applyPath(p) case Some(value) => value */ - case Value.Ref(l) => - newCtx.getLocalClosPath(l) match - case None => super.applyPath(p) - case Some(closPath) => Select(closPath, newCtx.getLocalSym(l).id)(N) + case Value.Ref(l) => newCtx.getLocalCaptureSym(l) match + case Some(captureSym) => Select(newCtx.getLocalClosPath(l).get, captureSym.id)(N) + case None => newCtx.getLocalPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) case _ => super.applyPath(p) - + + // rewrites references to block member symbols val transformer2 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = val (rewriten, syms) = rewriteBms(b, newCtx) @@ -329,7 +377,7 @@ class Lifter(using State): val transformed = blk |> transformer1.applyBlock |> transformer2.applyBlock - if ctx.usedLocals(f.sym).vars.size == 0 then + if thisVars.mutated.size == 0 then (FunDefn(f.owner, f.sym, f.params, transformed), newDefns) else val paramsSet = f.params.flatMap(_.paramSyms) @@ -339,6 +387,7 @@ class Lifter(using State): .rest(transformed) (FunDefn(f.owner, f.sym, f.params, bod), captureCls :: newDefns) + end liftDefnsInFn // top-level def transform(b: Block) = diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 5baf7d687..4a7493d5d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -1,6 +1,7 @@ :lift :js +:expect 5 :sjs fun f(used1, unused1) = let used2 = unused1 @@ -11,34 +12,26 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f1, g1, f$capture1; -//│ g1 = function g(f$capture2) { +//│ let f1, g1; +//│ g1 = function g(used1, used2) { //│ return (g$_arg) => { //│ let used3; //│ used3 = 2; -//│ return f$capture2.used10$ + f$capture2.used21$; +//│ return used1 + used2; //│ }; //│ }; -//│ f$capture1 = function f$capture(used10$1, used21$1) { return new f$capture.class(used10$1, used21$1); }; -//│ f$capture1.class = class f$capture { -//│ constructor(used10$, used21$) { -//│ this.used10$ = used10$; -//│ this.used21$ = used21$; -//│ } -//│ toString() { return "f$capture(" + this.used10$ + ", " + this.used21$ + ")"; } -//│ }; //│ f1 = function f(used1, unused1) { -//│ let unused2, tmp, capture, g$this; -//│ capture = new f$capture1(used1); -//│ capture.used21$ = unused1; +//│ let used2, unused2, tmp, g$this; +//│ used2 = unused1; //│ unused2 = 2; -//│ g$this = g1(capture) ?? null; -//│ tmp = g$this(capture.used21$); +//│ g$this = g1(used1, used2) ?? null; +//│ tmp = g$this(used2); //│ return tmp + unused2; //│ }; //│ f1(1, 2) //│ = 5 +:expect 5 :sjs fun f(used1, unused1) = let used2 = unused1 @@ -50,29 +43,20 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f3, g3, f$capture3; -//│ g3 = function g(f$capture4) { +//│ let f3, g3; +//│ g3 = function g(used1, used2) { //│ return (g$_arg) => { //│ let used3; //│ used3 = 2; -//│ return f$capture4.used10$ + f$capture4.used21$; +//│ return used1 + used2; //│ }; //│ }; -//│ f$capture3 = function f$capture(used10$1, used21$1) { return new f$capture.class(used10$1, used21$1); }; -//│ f$capture3.class = class f$capture { -//│ constructor(used10$, used21$) { -//│ this.used10$ = used10$; -//│ this.used21$ = used21$; -//│ } -//│ toString() { return "f$capture(" + this.used10$ + ", " + this.used21$ + ")"; } -//│ }; //│ f3 = function f(used1, unused1) { -//│ let unused2, foo, tmp, capture; -//│ capture = new f$capture3(used1); -//│ capture.used21$ = unused1; +//│ let used2, unused2, foo, tmp; +//│ used2 = unused1; //│ unused2 = 2; -//│ foo = g3(capture) ?? null; -//│ tmp = foo(capture.used21$) ?? null; +//│ foo = g3(used1, used2) ?? null; +//│ tmp = foo(used2) ?? null; //│ return tmp + unused2; //│ }; //│ f3(1, 2) @@ -123,33 +107,20 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f7, g7, f$capture5; -//│ g7 = function g(f$capture6) { +//│ let f7, g7; +//│ g7 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { //│ let tmp, tmp1, tmp2, tmp3; -//│ tmp = f$capture6.a10$ + f$capture6.a21$; -//│ tmp1 = tmp + f$capture6.a32$; -//│ tmp2 = tmp1 + f$capture6.a43$; -//│ tmp3 = tmp2 + f$capture6.a54$; -//│ return tmp3 + f$capture6.a65$; +//│ tmp = a1 + a2; +//│ tmp1 = tmp + a3; +//│ tmp2 = tmp1 + a4; +//│ tmp3 = tmp2 + a5; +//│ return tmp3 + a6; //│ }; //│ }; -//│ f$capture5 = function f$capture(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1) { return new f$capture.class(a10$1, a21$1, a32$1, a43$1, a54$1, a65$1); }; -//│ f$capture5.class = class f$capture { -//│ constructor(a10$, a21$, a32$, a43$, a54$, a65$) { -//│ this.a10$ = a10$; -//│ this.a21$ = a21$; -//│ this.a32$ = a32$; -//│ this.a43$ = a43$; -//│ this.a54$ = a54$; -//│ this.a65$ = a65$; -//│ } -//│ toString() { return "f$capture(" + this.a10$ + ", " + this.a21$ + ", " + this.a32$ + ", " + this.a43$ + ", " + this.a54$ + ", " + this.a65$ + ")"; } -//│ }; //│ f7 = function f(a1, a2, a3, a4, a5, a6) { -//│ let tmp, capture, g$this; -//│ capture = new f$capture5(a1, a2, a3, a4, a5, a6); -//│ g$this = g7(capture) ?? null; +//│ let tmp, g$this; +//│ g$this = g7(a1, a2, a3, a4, a5, a6) ?? null; //│ tmp = g$this(); //│ return tmp; //│ }; @@ -172,20 +143,20 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture7; -//│ g9 = function g(f$capture8) { +//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture1; +//│ g9 = function g(f$capture2) { //│ return () => { -//│ f$capture8.a0$ = 1; +//│ f$capture2.a0$ = 1; //│ return null; //│ }; //│ }; -//│ h1 = function h(f$capture8) { +//│ h1 = function h(f$capture2) { //│ return () => { -//│ return f$capture8.a0$; +//│ return f$capture2.a0$; //│ }; //│ }; -//│ f$capture7 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; -//│ f$capture7.class = class f$capture { +//│ f$capture1 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; +//│ f$capture1.class = class f$capture { //│ constructor(a0$) { //│ this.a0$ = a0$; //│ } @@ -193,7 +164,7 @@ x + y //│ }; //│ f9 = function f() { //│ let capture, g$this, h$this; -//│ capture = new f$capture7(); +//│ capture = new f$capture1(); //│ capture.a0$ = 0; //│ g$this = g9(capture) ?? null; //│ h$this = h1(capture) ?? null; @@ -263,3 +234,70 @@ a.toString() + b + c + d //│ > b: [Function (anonymous)] //│ ret = } +// some variables mutated, some not +:sjs +:expect 7 +fun f(unused, immutable, mutated) = + fun g() = + set mutated = 2 + immutable + mutated + fun h() = mutated + let a = g() + a + h() + unused +f(1, 2, 1000) +//│ JS (unsanitized): +//│ let f14, g13, h5, f$capture5; +//│ g13 = function g(immutable, f$capture6) { +//│ return () => { +//│ f$capture6.mutated0$ = 2; +//│ return immutable + f$capture6.mutated0$; +//│ }; +//│ }; +//│ h5 = function h(f$capture6) { +//│ return () => { +//│ return f$capture6.mutated0$; +//│ }; +//│ }; +//│ f$capture5 = function f$capture(mutated0$1) { return new f$capture.class(mutated0$1); }; +//│ f$capture5.class = class f$capture { +//│ constructor(mutated0$) { +//│ this.mutated0$ = mutated0$; +//│ } +//│ toString() { return "f$capture(" + this.mutated0$ + ")"; } +//│ }; +//│ f14 = function f(unused, immutable, mutated) { +//│ let a1, tmp21, tmp22, tmp23, capture, g$this, h$this; +//│ capture = new f$capture5(mutated); +//│ g$this = g13(immutable, capture) ?? null; +//│ tmp21 = g$this(); +//│ a1 = tmp21; +//│ h$this = h5(capture) ?? null; +//│ tmp22 = h$this(); +//│ tmp23 = a1 + tmp22; +//│ return tmp23 + unused; +//│ }; +//│ f14(1, 2, 1000) +//│ = 7 + +// if used as a higher order function, pass closures +:fixme +:expect 2 +fun f() = + let x = g + let y = 2 + fun g() = y + x() +f() +//│ ═══[RUNTIME ERROR] Expected: 2, got: null + +:fixme +:expect 2 +fun f() = + let y = 1 + fun g() = y + let x = g + set y = 2 + x() +f() +//│ ═══[RUNTIME ERROR] Expected: 2, got: 1 +//│ = 1 From 2a45a20f4455eca02e98f077a354457161c46688 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 28 Jan 2025 23:20:34 +0800 Subject: [PATCH 016/127] fix bug --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 16 ++++++++++++++-- .../shared/src/test/mlscript/codegen/Lifter.mls | 4 +--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 3444592a4..855917c96 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -13,6 +13,7 @@ import scala.collection.mutable.ListBuffer import scala.collection.mutable.LinkedHashSet import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap +import scala.collection.mutable.Set as MutSet import scala.annotation.nowarn // Lifts classes and functions to the top-level. @@ -126,12 +127,20 @@ class Lifter(using State): case Some(value) => for l <- vars do retMap(f).vars.addOne(l) for l <- mutated do retMap(f).mutated.addOne(l) - + + // tracks if the locals here have been mutated more than once in this function + val assignedOnce: MutSet[Local] = MutSet.empty + val assignedTwice: MutSet[Local] = MutSet.empty + def addLocal(l: Local, mut: Bool) = lookup.get(l) match case Some(f) => if mut then retMap(f).mutated.addOne(l) retMap(f).vars.addOne(l) - case None => () + case None => if mut then + if assignedOnce.contains(l) then + assignedTwice.add(l) + else + assignedOnce.add(l) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match @@ -155,6 +164,9 @@ class Lifter(using State): walker.applyBlock(f.body) + // add mutable locals + retMap(f).mutated ++= retMap(f).vars.intersect(assignedTwice) + retMap.toMap def findUsedLocals(b: Block): UsedLocalsMap = diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 4a7493d5d..02100584d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -290,7 +290,6 @@ fun f() = f() //│ ═══[RUNTIME ERROR] Expected: 2, got: null -:fixme :expect 2 fun f() = let y = 1 @@ -299,5 +298,4 @@ fun f() = set y = 2 x() f() -//│ ═══[RUNTIME ERROR] Expected: 2, got: 1 -//│ = 1 +//│ = 2 From f97182fcae4b2d468e2153e509842512613a7c12 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 02:48:27 +0800 Subject: [PATCH 017/127] add broken test --- .../src/main/scala/hkmc2/codegen/Block.scala | 4 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 25 ++++++++--- .../src/test/mlscript/codegen/Lifter.mls | 43 +++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 6cc1ffdc1..4ab9188a2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -173,11 +173,11 @@ sealed abstract class Defn: lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars - case ClsLikeDefn(own, isym, sym, k, paramsOpt, Nil, parentSym, + case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentSym, methods, privateFields, publicFields, preCtor, ctor) => preCtor.freeVars ++ ctor.freeVars ++ methods.flatMap(_.freeVars) - -- privateFields -- publicFields.map(_.sym) + -- privateFields -- publicFields.map(_.sym) -- auxParams.flatMap(_.paramSyms) final case class FunDefn( owner: Opt[InnerSymbol], diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 855917c96..7aa2c6d69 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -150,8 +150,9 @@ class Lifter(using State): case Define(c: ClsLikeDefn, rest) => for f <- c.methods do merge(findUsedLocalsImpl(f, lookupNext)) super.applyBlock(b) - case Assign(lhs, _, rest) => - addLocal(lhs, true) // TODO: for now, we just assume if a symbol is assigned to, then it's mutable + case Assign(lhs, rhs, rest) => + addLocal(lhs, true) // TODO: when proper immutable variables have been added, refactor this + applyResult(rhs) super.applyBlock(b) case _ => super.applyBlock(b) @@ -213,6 +214,19 @@ class Lifter(using State): def liftDefnsCls(c: ClsLikeDefn, ctx: LifterCtx): List[Defn] = ??? + private val clsLikeCache: MutMap[Local, Set[Local]] = MutMap.empty + def getInnerSymbols(c: ClsLikeDefn) = clsLikeCache.get(c.isym) match + case Some(value) => value + case None => + val ret: Set[Local] = c.freeVars.collect: + case s: InnerSymbol => s + case t: TermSymbol if t.owner.isDefined => t.owner.get + clsLikeCache.addOne(c.isym -> ret) + ret + + private def needsClsCapture(captureCls: ClsLikeDefn, candidate: ClsLikeDefn) = + getInnerSymbols(candidate).contains(captureCls.isym) + private def needsCapture(captureFn: FunDefn, candidate: Defn, ctx: LifterCtx) = val candVars = candidate.freeVars val captureFnVars = ctx.usedLocals(captureFn.sym).mutated.toSet @@ -240,7 +254,7 @@ class Lifter(using State): object Lifted: def of(d: Defn, ed: List[Defn]) = Lifted(d, ed, N) - inline def liftOutDefn(base: FunDefn, d: Defn, ctx: LifterCtx): Lifted = + inline def liftOutDefn(base: Defn, d: Defn, ctx: LifterCtx): Lifted = @nowarn("msg=New anonymous class definition will be duplicated at each inline site") // inlined only at one place val includedCaptures = ctx.prevFnDefns.collect: case prev if needsCapture(prev, d, ctx) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) @@ -274,8 +288,9 @@ class Lifter(using State): val (lifted, extra) = liftDefnsInFn(newDef, newCtx) Lifted.of(lifted, extra).withInfo(includedCaptures.map(_._1.sym), includedLocals.map(_._1)) case d: ClsLikeDefn => Lifted.of(d, Nil) - // TODO - // liftDefnsCls(d) + // val newDef = ClsLikeDefn( + // base.owner, d.isym, d.sym, d.k, + // ) case _ => Lifted.of(d, Nil) def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 02100584d..105afc98a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -299,3 +299,46 @@ fun f() = x() f() //│ = 2 + +:sjs +:expect 2 +:fixme +fun f(arg) = + fun g(n) = if n <= 0 then arg else h(n-1) + fun h(n) = if n <= 0 then arg else g(n-1) + h(5) +f(2) +//│ JS (unsanitized): +//│ let f20, g19, h7; +//│ g19 = function g(arg) { +//│ return (n) => { +//│ let scrut, tmp21; +//│ scrut = n <= 0; +//│ if (scrut === true) { +//│ return arg; +//│ } else { +//│ tmp21 = n - 1; +//│ return h7(tmp21); +//│ } +//│ }; +//│ }; +//│ h7 = function h(arg) { +//│ return (n) => { +//│ let scrut, tmp21; +//│ scrut = n <= 0; +//│ if (scrut === true) { +//│ return arg; +//│ } else { +//│ tmp21 = n - 1; +//│ return g19(tmp21); +//│ } +//│ }; +//│ }; +//│ f20 = function f(arg) { +//│ let h$this; +//│ h$this = h7(arg) ?? null; +//│ return h$this(5); +//│ }; +//│ f20(2) +//│ ═══[RUNTIME ERROR] Expected: 2, got: [Function (anonymous)] +//│ = [Function (anonymous)] From 9cbad67b6982a66faa62c2c908378c11e0246f63 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 03:19:11 +0800 Subject: [PATCH 018/127] refactor and fix huge bug --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 132 +++++++++--------- .../src/test/mlscript/codegen/Lifter.mls | 14 +- 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 7aa2c6d69..894146dd1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -72,8 +72,6 @@ class Lifter(using State): def getLocalClosPath(l: Local) = lookup(l).flatMap(capturePaths.get(_)) // the symbol in the capture corresponding to a particular local def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) - // the path to a local value containing this function with the captures already applied - def getBmsPath(b: BlockMemberSymbol) = bmsPaths.get(b) // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) @@ -83,7 +81,6 @@ class Lifter(using State): def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = copy(capturePaths = paths) def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) - def addBmsPaths(paths: Map[BlockMemberSymbol, Path]) = copy(bmsPaths = bmsPaths ++ paths) def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) @@ -184,6 +181,17 @@ class Lifter(using State): walker.applyBlock(b) UsedLocalsMap.from(usedMap) + def generateLiftInfo(b: Block, ctx: LifterCtx) = + var ret: Map[BlockMemberSymbol, LiftedInfo] = Map.empty + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(f: FunDefn, rest) => + ret = ret ++ createdLiftInfoFn(f, ctx) + super.applyBlock(b) + case _ => super.applyBlock(b) + walker.applyBlock(b) + ret + def createCaptureCls(f: FunDefn, ctx: LifterCtx) = val nme = f.sym.nme + "$capture" @@ -244,60 +252,73 @@ class Lifter(using State): val reqdCaptures: List[BlockMemberSymbol], val reqdVars: List[Local] ) + case class Lifted( val liftedDefn: Defn, val extraDefns: List[Defn], - val info: Opt[LiftedInfo] - ): - def withInfo = LiftedInfo.apply.tupled andThen (info => Lifted(liftedDefn, extraDefns, S(info))) + ) - object Lifted: - def of(d: Defn, ed: List[Defn]) = Lifted(d, ed, N) - inline def liftOutDefn(base: Defn, d: Defn, ctx: LifterCtx): Lifted = - @nowarn("msg=New anonymous class definition will be duplicated at each inline site") // inlined only at one place - val includedCaptures = ctx.prevFnDefns.collect: - case prev if needsCapture(prev, d, ctx) => (prev, VarSymbol(Tree.Ident(prev.sym.nme + "$capture"))) + def createLiftInfoCont(d: Defn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + val includedCaptures = ctx.prevFnDefns.filter(needsCapture(_, d, ctx)) val includedLocals = ctx.prevFnDefns.flatMap: ls => - neededImutLocals(ls, d, ctx).map: l => - (l, VarSymbol(Tree.Ident(l.nme))) + neededImutLocals(ls, d, ctx) - if includedCaptures.isEmpty && includedLocals.isEmpty then Lifted.of(d, Nil) - else - val extraParamsCaptures = includedCaptures.map: - case (d, sym) => Param(FldFlags.empty, sym, None) - val newCapturePaths = includedCaptures.map: - case (d, sym) => d.sym -> sym.asPath - .toMap - - val extraParamsLocals = includedLocals.map: - case (d, sym) => Param(FldFlags.empty, sym, None) - val newLocalsPaths = includedLocals.map: - case (d, sym) => d -> sym - .toMap - - val extraParams = extraParamsLocals ++ extraParamsCaptures - - d match - case d: FunDefn => - val newDef = FunDefn( - base.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body - ) - val newCtx = ctx.replCapturePaths(newCapturePaths).replLocalPaths(newLocalsPaths) - val (lifted, extra) = liftDefnsInFn(newDef, newCtx) - Lifted.of(lifted, extra).withInfo(includedCaptures.map(_._1.sym), includedLocals.map(_._1)) - case d: ClsLikeDefn => Lifted.of(d, Nil) - // val newDef = ClsLikeDefn( - // base.owner, d.isym, d.sym, d.k, - // ) - case _ => Lifted.of(d, Nil) + if includedCaptures.isEmpty && includedLocals.isEmpty then Map.empty + else d match + case f: FunDefn => createdLiftInfoFn(f, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) + case c: ClsLikeDefn => Map.empty + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) + case _ => Map.empty + def createdLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + val (_, defns) = f.body.floatOutDefns + defns.flatMap(createLiftInfoCont(_, ctx.addDefn(f))).toMap + def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = val info = ctx.getBmsReqdInfo(sym).get val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) Call(sym.asPath, localsArgs ++ capturesArgs)(false) + + def liftOutDefn(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match + case N => Lifted(d, Nil) + case S(LiftedInfo(includedCaptures, includedLocals)) => + val capturesSymbols = includedCaptures.map: sym => + (sym, VarSymbol(Tree.Ident(sym.nme + "$capture"))) + + val localsSymbols = includedLocals.map:sym => + (sym, VarSymbol(Tree.Ident(sym.nme))) + + if includedCaptures.isEmpty && includedLocals.isEmpty then Lifted(d, Nil) + else + val extraParamsCaptures = capturesSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym.asPath + .toMap + + val extraParamsLocals = localsSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newLocalsPaths = localsSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym + .toMap + + val extraParams = extraParamsLocals ++ extraParamsCaptures + + d match + case d: FunDefn => + val newDef = FunDefn( + base.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body + ) + val newCtx = ctx.replCapturePaths(newCapturePaths).replLocalPaths(newLocalsPaths) + val (lifted, extra) = liftDefnsInFn(newDef, newCtx) + Lifted(lifted, extra) + case d: ClsLikeDefn => Lifted(d, Nil) + // val newDef = ClsLikeDefn( + // base.owner, d.isym, d.sym, d.k, + // ) + case _ => Lifted(d, Nil) def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): (Defn, List[Defn]) = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) @@ -312,33 +333,13 @@ class Lifter(using State): val nestedCtx = captureCtx.addDefn(f) val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) - val bmsInfo = nestedLifted.collect: - case Lifted(liftedDefn, extraDefns, S(info)) => - liftedDefn.sym -> info - .toMap val newDefns = nestedLifted.flatMap: - case Lifted(liftedDefn, extraDefns, _) => liftedDefn :: extraDefns - - // creates the triple: - // (bms, that bms's required captures, the symbol to that bms with captures applied) - val withSymbols = bmsInfo.map: (bms, captures) => - (bms, captures, FlowSymbol(bms.nme + "$this")) - - val bmsPathsMap = withSymbols.map: - case (bms, captures, sym) => bms -> sym.asPath - .toMap + case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns val thisVars = ctx.usedLocals(f.sym) val newCtx = captureCtx - .addBmsReqdInfo(bmsInfo.toMap) - .addBmsPaths(bmsPathsMap) .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) - - - val start = withSymbols.foldRight(blockBuilder): - case ((bms, captures, sym), acc) => - acc.assign(sym, Call(bms.asPath, captures.reqdCaptures.map(newCtx.getCapturePath(_).get.asArg))(false)) // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, @@ -419,11 +420,12 @@ class Lifter(using State): // top-level def transform(b: Block) = val ctx = LifterCtx.withLocals(findUsedLocals(b)) + val ctxx = ctx.addBmsReqdInfo(generateLiftInfo(b, ctx)) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - val (d, extra) = liftDefnsInFn(f, ctx) + val (d, extra) = liftDefnsInFn(f, ctxx) (d :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(b) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 105afc98a..63f038b1e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -302,7 +302,6 @@ f() :sjs :expect 2 -:fixme fun f(arg) = fun g(n) = if n <= 0 then arg else h(n-1) fun h(n) = if n <= 0 then arg else g(n-1) @@ -312,25 +311,27 @@ f(2) //│ let f20, g19, h7; //│ g19 = function g(arg) { //│ return (n) => { -//│ let scrut, tmp21; +//│ let scrut, tmp21, h$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { //│ tmp21 = n - 1; -//│ return h7(tmp21); +//│ h$this = h7(arg) ?? null; +//│ return h$this(tmp21); //│ } //│ }; //│ }; //│ h7 = function h(arg) { //│ return (n) => { -//│ let scrut, tmp21; +//│ let scrut, tmp21, g$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { //│ tmp21 = n - 1; -//│ return g19(tmp21); +//│ g$this = g19(arg) ?? null; +//│ return g$this(tmp21); //│ } //│ }; //│ }; @@ -340,5 +341,4 @@ f(2) //│ return h$this(5); //│ }; //│ f20(2) -//│ ═══[RUNTIME ERROR] Expected: 2, got: [Function (anonymous)] -//│ = [Function (anonymous)] +//│ = 2 From f3bf942810eee7732b10547668f1c91755a04273 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 04:37:14 +0800 Subject: [PATCH 019/127] small progress and fix typo --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 894146dd1..8236e9762 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -75,7 +75,8 @@ class Lifter(using State): // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) - def addDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) + def addFnDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) + 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, Path]) = copy(capturePaths = paths) @@ -186,7 +187,7 @@ class Lifter(using State): val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - ret = ret ++ createdLiftInfoFn(f, ctx) + ret = ret ++ createLiftInfoFn(f, ctx) super.applyBlock(b) case _ => super.applyBlock(b) walker.applyBlock(b) @@ -267,13 +268,18 @@ class Lifter(using State): if includedCaptures.isEmpty && includedLocals.isEmpty then Map.empty else d match - case f: FunDefn => createdLiftInfoFn(f, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) + case f: FunDefn => createLiftInfoFn(f, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) case c: ClsLikeDefn => Map.empty + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) case _ => Map.empty - def createdLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOutDefns - defns.flatMap(createLiftInfoCont(_, ctx.addDefn(f))).toMap + defns.flatMap(createLiftInfoCont(_, ctx.addFnDefn(f))).toMap + + def createdLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + val defns = c.preCtor.floatOutDefns._2 ++ c.ctor.floatOutDefns._2 + defns.flatMap(f => createLiftInfoCont(f, ctx.addClsDefn(c))).toMap + ++ c.methods.flatMap(f => createLiftInfoFn(f, ctx.addClsDefn(c))) def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = val info = ctx.getBmsReqdInfo(sym).get @@ -330,7 +336,7 @@ class Lifter(using State): val captureCtx = ctx .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture - val nestedCtx = captureCtx.addDefn(f) + val nestedCtx = captureCtx.addFnDefn(f) val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) val newDefns = nestedLifted.flatMap: From cc55d1e112e50a1f89ec2e74cf1e4098ef5e8037 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 14:02:47 +0800 Subject: [PATCH 020/127] re-organization and some documentation --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 91 +++++++++++++------ 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 8236e9762..e9c27692b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -181,18 +181,15 @@ class Lifter(using State): case _ => super.applyBlock(b) walker.applyBlock(b) UsedLocalsMap.from(usedMap) - - def generateLiftInfo(b: Block, ctx: LifterCtx) = - var ret: Map[BlockMemberSymbol, LiftedInfo] = Map.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Define(f: FunDefn, rest) => - ret = ret ++ createLiftInfoFn(f, ctx) - super.applyBlock(b) - case _ => super.applyBlock(b) - walker.applyBlock(b) - ret - + + /** + * 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 correpsonding `VarSymbol` in the class, and + * `varsList` specifies the order of these variables in the class's constructor. + */ def createCaptureCls(f: FunDefn, ctx: LifterCtx) = val nme = f.sym.nme + "$capture" @@ -224,6 +221,12 @@ class Lifter(using State): def liftDefnsCls(c: ClsLikeDefn, ctx: LifterCtx): List[Defn] = ??? private val clsLikeCache: 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: ClsLikeDefn) = clsLikeCache.get(c.isym) match case Some(value) => value case None => @@ -233,16 +236,34 @@ class Lifter(using State): clsLikeCache.addOne(c.isym -> ret) ret - private def needsClsCapture(captureCls: ClsLikeDefn, candidate: ClsLikeDefn) = - getInnerSymbols(candidate).contains(captureCls.isym) - - private def needsCapture(captureFn: FunDefn, candidate: Defn, ctx: LifterCtx) = - val candVars = candidate.freeVars + /** + * 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: ClsLikeDefn) = + 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).mutated.toSet !candVars.intersect(captureFnVars).isEmpty - private def neededImutLocals(captureFn: FunDefn, candidate: Defn, ctx: LifterCtx) = - val candVars = candidate.freeVars + /** + * Gets the immutable local variables of a function that need to 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.mutated.toSet val imutVars = captureFnVars.vars @@ -259,7 +280,6 @@ class Lifter(using State): val extraDefns: List[Defn], ) - def createLiftInfoCont(d: Defn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val includedCaptures = ctx.prevFnDefns.filter(needsCapture(_, d, ctx)) @@ -269,17 +289,31 @@ class Lifter(using State): if includedCaptures.isEmpty && includedLocals.isEmpty then Map.empty else d match case f: FunDefn => createLiftInfoFn(f, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) - case c: ClsLikeDefn => Map.empty + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) + case c: ClsLikeDefn => + val clsCaptures = ctx.prevClsDefns.filter(needsClsCapture(_, c)).map(_.isym) + createLiftInfoCls(c, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), clsCaptures ++ includedLocals)) case _ => Map.empty def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOutDefns defns.flatMap(createLiftInfoCont(_, ctx.addFnDefn(f))).toMap - - def createdLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + + def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = c.preCtor.floatOutDefns._2 ++ c.ctor.floatOutDefns._2 - defns.flatMap(f => createLiftInfoCont(f, ctx.addClsDefn(c))).toMap - ++ c.methods.flatMap(f => createLiftInfoFn(f, ctx.addClsDefn(c))) + val newCtx = ctx.addClsDefn(c) + defns.flatMap(f => createLiftInfoCont(f, newCtx)).toMap + ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) + + def createLiftInfo(b: Block, ctx: LifterCtx) = + var ret: Map[BlockMemberSymbol, LiftedInfo] = Map.empty + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(f: FunDefn, rest) => + ret = ret ++ createLiftInfoFn(f, ctx) + super.applyBlock(b) + case _ => super.applyBlock(b) + walker.applyBlock(b) + ret def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = val info = ctx.getBmsReqdInfo(sym).get @@ -338,12 +372,13 @@ class Lifter(using State): .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture val nestedCtx = captureCtx.addFnDefn(f) + // lift out the nested defns val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) val newDefns = nestedLifted.flatMap: case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns + // some book-keeping val thisVars = ctx.usedLocals(f.sym) - val newCtx = captureCtx .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) @@ -414,8 +449,10 @@ class Lifter(using State): if thisVars.mutated.size == 0 then (FunDefn(f.owner, f.sym, f.params, transformed), newDefns) else + // move the function's parameters to the capture val paramsSet = f.params.flatMap(_.paramSyms) val paramsList = varsList.filter(paramsSet.contains(_)) + // moved when the capture is instantiated val bod = blockBuilder .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList.map(_.asPath))) .rest(transformed) @@ -426,7 +463,7 @@ class Lifter(using State): // top-level def transform(b: Block) = val ctx = LifterCtx.withLocals(findUsedLocals(b)) - val ctxx = ctx.addBmsReqdInfo(generateLiftInfo(b, ctx)) + val ctxx = ctx.addBmsReqdInfo(createLiftInfo(b, ctx)) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match From f09b1a6799cea66a396e95fe5d70f523f7bcc9ff Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 17:05:06 +0800 Subject: [PATCH 021/127] progess on class lifting --- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 303 ++++++++++++------ .../src/main/scala/hkmc2/semantics/Term.scala | 2 +- .../src/test/mlscript/codegen/Lifter.mls | 206 ++++++++---- 4 files changed, 350 insertions(+), 163 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 4ab9188a2..049fd18b9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -197,7 +197,7 @@ final case class ValDefn( final case class ClsLikeDefn( owner: Opt[InnerSymbol], - isym: MemberSymbol[? <: ClassLikeDef], + isym: MemberSymbol[? <: ClassLikeDef] & InnerSymbol, sym: BlockMemberSymbol, k: syntax.ClsLikeKind, paramsOpt: Opt[ParamList], diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index e9c27692b..4a9499fce 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -36,33 +36,35 @@ class Lifter(using State): case fn -> vars => vars.vars.map(v => v -> fn) // gets the function to which a local belongs def lookup(l: Local) = inverse.get(l) - def print = println(mp.map: - case a -> b => a -> b - ) object UsedLocalsMap: def from(mp: Map[FunDefn, FreeVars]) = UsedLocalsMap(mp.map: case a -> b => a.sym -> b ) - - // usedLocals: describes the locals belonging to each function that are accessed/mutated by nested defns - // localCaptureSyms: the symbols in a capture corresponding to a particular local - // prevFnDefns: fun defns that have already been traversed - // prevClsDefns: class defns that have already been traversed - // capturePaths: the path to access a particular function's capture in the local scope - // bmsReqdInfo: the (mutable) captures and (immutable) local variables each function requires - // bmsPaths: the path to access a particular BlockMemberSymbol in the local scope w/ the captures already applied - // localPaths: the path to access a particular local (possibly belonging to a prev fn/class) in the current scope + + /** + * The context of the class lifter. + * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested defns + * @param localCaptureSyms The symbols in a capture corresponding to a particular local + * @param prevFnDefns Function definitoins that have already been traversed + * @param prevClsDefns Class definitions that have already been traversed + * @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 bmsPaths The path to access a particular BlockMemberSymbol in the local scope with the captures already applied + * @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 + */ case class LifterCtx( val usedLocals: UsedLocalsMap, - val localCaptureSyms: Map[Local, VarSymbol], + val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol], val prevFnDefns: List[FunDefn], val prevClsDefns: List[ClsLikeDefn], val capturePaths: Map[BlockMemberSymbol, Path], val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures val bmsPaths: Map[BlockMemberSymbol, Path], - val localPaths: Map[Local, Local] + val localPaths: Map[Local, Local], + val iSymPaths: Map[InnerSymbol, Local] ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -74,19 +76,22 @@ class Lifter(using State): def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) + def getIsymPath(l: InnerSymbol) = iSymPaths.get(l) def addFnDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) - def addLocalCaptureSyms(m: Map[Local, VarSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) + def addLocalCaptureSyms(m: Map[Local, LocalSymbol & NamedSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) def getBmsReqdInfo(sym: BlockMemberSymbol) = bmsReqdInfo.get(sym) def replCapturePaths(paths: Map[BlockMemberSymbol, Path]) = copy(capturePaths = paths) def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) + def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(iSymPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) - + object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty) + def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, + Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) def getVars(f: FunDefn): Set[Local] = @@ -202,9 +207,9 @@ class Lifter(using State): val fresh = FreshInt() - val varsMap: Map[Local, VarSymbol] = mutated.map: s => + val varsMap: Map[Local, TermSymbol] = mutated.map: s => val id = fresh.make - s -> VarSymbol(Tree.Ident(s.nme + id + "$")) + s -> TermSymbol(syntax.ParamBind, S(clsSym), Tree.Ident(s.nme + id + "$")) .toMap val varsList = mutated.toList @@ -218,22 +223,20 @@ class Lifter(using State): (defn, varsMap, varsList) - def liftDefnsCls(c: ClsLikeDefn, ctx: LifterCtx): List[Defn] = ??? - - private val clsLikeCache: MutMap[Local, Set[Local]] = MutMap.empty + private val clsLikeCache: MutMap[Defn, 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: ClsLikeDefn) = clsLikeCache.get(c.isym) match + def getInnerSymbols(c: Defn) = clsLikeCache.get(c) match case Some(value) => value case None => val ret: Set[Local] = c.freeVars.collect: case s: InnerSymbol => s case t: TermSymbol if t.owner.isDefined => t.owner.get - clsLikeCache.addOne(c.isym -> ret) + clsLikeCache.addOne(c -> ret) ret /** @@ -242,7 +245,7 @@ class Lifter(using State): * @param liftDefn The class being lifted. * @return Whether the class needs to be captured. */ - private def needsClsCapture(captureCls: ClsLikeDefn, liftDefn: ClsLikeDefn) = + private def needsClsCapture(captureCls: ClsLikeDefn, liftDefn: Defn) = getInnerSymbols(liftDefn).contains(captureCls.isym) /** @@ -272,7 +275,8 @@ class Lifter(using State): case class LiftedInfo( val reqdCaptures: List[BlockMemberSymbol], - val reqdVars: List[Local] + val reqdVars: List[Local], + val reqdInnerSyms: List[InnerSymbol] ) case class Lifted( @@ -280,36 +284,66 @@ class Lifter(using State): val extraDefns: List[Defn], ) - def createLiftInfoCont(d: Defn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + + // the commented code is incorrect, more in-depth analysis is needed to properly remove variables/captures + // that aren't needed + /* val includedCaptures = ctx.prevFnDefns.filter(needsCapture(_, d, ctx)) val includedLocals = ctx.prevFnDefns.flatMap: ls => neededImutLocals(ls, d, ctx) - if includedCaptures.isEmpty && includedLocals.isEmpty then Map.empty + val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.filter(needsClsCapture(_, d)).map(_.isym).collect: + // this line is just to satisfy the type system, in reality anything we capture is an InnerSymbol + case s: InnerSymbol => s + + val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) + */ + + // for now, we just include everything + val includedCaptures = ctx.prevFnDefns.filter: f => + val FreeVars(vars, mut) = ctx.usedLocals(f.sym) + mut.size != 0 + + val includedLocals = ctx.prevFnDefns.flatMap: f => + val FreeVars(vars, mut) = ctx.usedLocals(f.sym) + vars.filter(!mut.contains(_)) + + val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym).filter: c => + parentCls match + case Some(value) if d.isInstanceOf[FunDefn] => value != parentCls + case _ => true + .collect: + // this line is just to satisfy the type system, in reality anything we capture is an InnerSymbol + case s: InnerSymbol => s + + val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) + + if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then Map.empty else d match - case f: FunDefn => createLiftInfoFn(f, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), includedLocals)) + case f: FunDefn => + createLiftInfoFn(f, parentCls, ctx) + (d.sym -> info) case c: ClsLikeDefn => - val clsCaptures = ctx.prevClsDefns.filter(needsClsCapture(_, c)).map(_.isym) - createLiftInfoCls(c, ctx) + (d.sym -> LiftedInfo(includedCaptures.map(_.sym), clsCaptures ++ includedLocals)) + createLiftInfoCls(c, ctx) + (d.sym -> info) case _ => Map.empty - - def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + + def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOutDefns - defns.flatMap(createLiftInfoCont(_, ctx.addFnDefn(f))).toMap + defns.flatMap(createLiftInfoCont(_, parentCls, ctx.addFnDefn(f))).toMap def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = c.preCtor.floatOutDefns._2 ++ c.ctor.floatOutDefns._2 val newCtx = ctx.addClsDefn(c) - defns.flatMap(f => createLiftInfoCont(f, newCtx)).toMap - ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) + defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap + ++ c.methods.flatMap(f => createLiftInfoFn(f, S(c), newCtx)) def createLiftInfo(b: Block, ctx: LifterCtx) = var ret: Map[BlockMemberSymbol, LiftedInfo] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - ret = ret ++ createLiftInfoFn(f, ctx) + ret = ret ++ createLiftInfoFn(f, N, ctx) super.applyBlock(b) case _ => super.applyBlock(b) walker.applyBlock(b) @@ -321,67 +355,98 @@ class Lifter(using State): val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) Call(sym.asPath, localsArgs ++ capturesArgs)(false) - def liftOutDefn(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match + // deals with creating parameter lists + def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match case N => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures)) => + val createSym = d match + case d: ClsLikeDefn => ((nme: String) => TermSymbol(syntax.ParamBind, S(d.isym), Tree.Ident(nme))) + case _ => ((nme: String) => VarSymbol(Tree.Ident(nme))) + val capturesSymbols = includedCaptures.map: sym => - (sym, VarSymbol(Tree.Ident(sym.nme + "$capture"))) + (sym, createSym(sym.nme + "$capture")) val localsSymbols = includedLocals.map:sym => - (sym, VarSymbol(Tree.Ident(sym.nme))) - - if includedCaptures.isEmpty && includedLocals.isEmpty then Lifted(d, Nil) - else - val extraParamsCaptures = capturesSymbols.map: // parameter list - case (d, sym) => Param(FldFlags.empty, sym, None) - val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym.asPath - .toMap - - val extraParamsLocals = localsSymbols.map: // parameter list - case (d, sym) => Param(FldFlags.empty, sym, None) - val newLocalsPaths = localsSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym - .toMap - - val extraParams = extraParamsLocals ++ extraParamsCaptures - - d match - case d: FunDefn => - val newDef = FunDefn( - base.owner, d.sym, PlainParamList(extraParams) :: d.params, d.body - ) - val newCtx = ctx.replCapturePaths(newCapturePaths).replLocalPaths(newLocalsPaths) - val (lifted, extra) = liftDefnsInFn(newDef, newCtx) - Lifted(lifted, extra) - case d: ClsLikeDefn => Lifted(d, Nil) - // val newDef = ClsLikeDefn( - // base.owner, d.isym, d.sym, d.k, - // ) - case _ => Lifted(d, Nil) + (sym, createSym(sym.nme)) + + val isymSymbols = clsCaptures.map:sym => + (sym, createSym(sym.nme + "$instance")) + + val extraParamsCaptures = capturesSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym.asPath + .toMap + + val extraParamsLocals = localsSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newLocalsPaths = localsSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym + .toMap + + val extraParamsIsyms = isymSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newIsymPaths = isymSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym + .toMap + + val extraParams = extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures + + val newCtx = ctx + .replCapturePaths(newCapturePaths) + .replLocalPaths(newLocalsPaths) + .replIsymPaths(newIsymPaths) + + d match + case f: FunDefn => + val newDef = FunDefn( + base.owner, f.sym, PlainParamList(extraParams) :: f.params, f.body + ) + liftDefnsInFn(newDef, newCtx) + case c: ClsLikeDefn => + val newDef = c.copy( + owner = base.owner, auxParams = c.auxParams.appended(PlainParamList(extraParams)) + ) + liftDefnsInCls(newDef, newCtx) + case _ => Lifted(d, Nil) - def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): (Defn, List[Defn]) = - val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) + def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted = + val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns + val (ctor, ctorDefns) = c.ctor.floatOutDefns - val (blk, nested) = f.body.floatOutDefns - - // add the mapping from this function's locals to the capture's symbols and the capture path - val captureSym = FlowSymbol("capture") - val captureCtx = ctx - .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on - .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture - val nestedCtx = captureCtx.addFnDefn(f) + val newCtx = ctx // TODO: add block member symbol replacement - // lift out the nested defns - val nestedLifted = nested.map(liftOutDefn(f, _, nestedCtx)) - val newDefns = nestedLifted.flatMap: - case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns + val newPreCtor = rewriteBlk(preCtor, newCtx) + val newCtor = rewriteBlk(ctor, newCtx) - // some book-keeping - val thisVars = ctx.usedLocals(f.sym) - val newCtx = captureCtx - .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) + val ctorDefnsLifted = (preCtorDefns ++ ctorDefns).flatMap: defn => + val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) + liftedDefn :: extraDefns + + val fLifted = c.methods.flatMap: f => + val Lifted(liftedDefn, extraDefns) = liftDefnsInFn(f, newCtx) + liftedDefn :: extraDefns + + val allDefs = (ctorDefnsLifted ++ fLifted).map: + case f: FunDefn => f.copy(owner = S(c.isym)) + case c: ClsLikeDefn => c.copy(owner = c.owner) + case d => d + + val funDefs = allDefs.collect: + case f: FunDefn => f + + val clsDefs = allDefs.collect: + case c: ClsLikeDefn => c + val newDef = c.copy( + methods = funDefs, + preCtor = newPreCtor, + ctor = newCtor + ) + + Lifted(newDef, clsDefs) + + def rewriteBlk(b: Block, ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, // it also directly rewrites Results. @@ -393,7 +458,7 @@ class Lifter(using State): // if possible, directly create the call and replace the result with it override def applyResult(r: Result): Result = r match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, newCtx) + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier @@ -410,14 +475,18 @@ class Lifter(using State): (walker.applyBlock(b), syms.toList) end rewriteBms - // rewrites references to variables val transformer1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match - case Assign(lhs, rhs, rest) => newCtx.getLocalCaptureSym(lhs) match + case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + case None => super.applyBlock(b) + + case Assign(lhs, rhs, rest) => + ctx.getLocalCaptureSym(lhs) match case Some(captureSym) => - AssignField(newCtx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) - case None => newCtx.getLocalPath(lhs) match + AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) + case None => ctx.getLocalPath(lhs) match case None => super.applyBlock(b) case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) case _ => super.applyBlock(b) @@ -428,9 +497,12 @@ class Lifter(using State): case None => super.applyPath(p) case Some(value) => value */ - case Value.Ref(l) => newCtx.getLocalCaptureSym(l) match - case Some(captureSym) => Select(newCtx.getLocalClosPath(l).get, captureSym.id)(N) - case None => newCtx.getLocalPath(l) match + case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + case Value.Ref(l) => ctx.getLocalCaptureSym(l) match + case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) + case None => ctx.getLocalPath(l) match case Some(value) => Value.Ref(value) case None => super.applyPath(p) case _ => super.applyPath(p) @@ -438,16 +510,41 @@ class Lifter(using State): // rewrites references to block member symbols val transformer2 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = - val (rewriten, syms) = rewriteBms(b, newCtx) + val (rewriten, syms) = rewriteBms(b, ctx) val pre = syms.foldLeft(blockBuilder): case (blk, (bms, local)) => - blk.assign(local, createCall(bms, newCtx)) + blk.assign(local, createCall(bms, ctx)) pre.rest(super.applyBlock(rewriten)) - val transformed = blk |> transformer1.applyBlock |> transformer2.applyBlock + b |> transformer1.applyBlock |> transformer2.applyBlock + + + def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted = + val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) + + val (blk, nested) = f.body.floatOutDefns + + // add the mapping from this function's locals to the capture's symbols and the capture path + val captureSym = FlowSymbol("capture") + val captureCtx = ctx + .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on + .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture + val nestedCtx = captureCtx.addFnDefn(f) + + // lift out the nested defns + val nestedLifted = nested.map(liftOutDefnCont(f, _, nestedCtx)) + val newDefns = nestedLifted.flatMap: + case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns + + // some book-keeping + val thisVars = ctx.usedLocals(f.sym) + val newCtx = captureCtx + .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) + + val transformed = rewriteBlk(blk, newCtx) if thisVars.mutated.size == 0 then - (FunDefn(f.owner, f.sym, f.params, transformed), newDefns) + Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) else // move the function's parameters to the capture val paramsSet = f.params.flatMap(_.paramSyms) @@ -456,7 +553,7 @@ class Lifter(using State): val bod = blockBuilder .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList.map(_.asPath))) .rest(transformed) - (FunDefn(f.owner, f.sym, f.params, bod), captureCls :: newDefns) + Lifted(FunDefn(f.owner, f.sym, f.params, bod), captureCls :: newDefns) end liftDefnsInFn @@ -468,7 +565,7 @@ class Lifter(using State): val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => - val (d, extra) = liftDefnsInFn(f, ctxx) + val Lifted(d, extra) = liftDefnsInFn(f, ctxx) (d :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(b) \ No newline at end of file diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 5936a7f13..15d4e8bed 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -286,7 +286,7 @@ sealed abstract class TypeLikeDef extends Definition: sealed abstract class ClassLikeDef extends TypeLikeDef: val owner: Opt[InnerSymbol] - val sym: MemberSymbol[? <: ClassLikeDef] + val sym: MemberSymbol[? <: ClassLikeDef] & InnerSymbol val bsym: BlockMemberSymbol val paramsOpt: Opt[ParamList] val tparams: Ls[TyParam] diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 63f038b1e..1eadb7532 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -63,6 +63,8 @@ f(1, 2) //│ = 5 // don't touch g if not needed +// CURRENTLY NOT WORKING +:todo :sjs fun f(used1, unused1) = let used2 = unused1 @@ -98,7 +100,44 @@ fun f(used1, unused1) = fun get() = used2 + used1 Test() f(1, 2).get() -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: used2 (class hkmc2.semantics.VarSymbol) +//│ JS (unsanitized): +//│ let f7, Test1, g7, h1, tmp; +//│ h1 = function h(used3, used1, used2) { +//│ return () => { +//│ return used3; +//│ }; +//│ }; +//│ g7 = function g(used1, used2) { +//│ return (g$_arg) => { +//│ let used3, tmp1, h$this; +//│ used3 = 2; +//│ h$this = h1(used3, used1, used2) ?? null; +//│ tmp1 = h$this(); +//│ return used1 + tmp1; +//│ }; +//│ }; +//│ Test1 = function Test() { (used11, used21) => {return new Test.class(, used11, used21); +//│ } }; +//│ Test1.class = class Test { +//│ constructor(used1, used2) { +//│ this.used1 = used1; +//│ this.used2 = used2; +//│ } +//│ get() { +//│ return this.used2 + this.used1; +//│ } +//│ toString() { return "Test(" + this.used1 + ", " + this.used2 + ")"; } +//│ }; +//│ f7 = function f(used1, unused1) { +//│ let used2, unused2, Test$this; +//│ used2 = unused1; +//│ unused2 = 2; +//│ Test$this = Test1(used1, used2) ?? null; +//│ return Test$this(); +//│ }; +//│ tmp = f7(1, 2); +//│ tmp.get() ?? null +//│ ═══[RUNTIME ERROR] TypeError: Test$this is not a function // preserve order :sjs @@ -107,24 +146,24 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f7, g7; -//│ g7 = function g(a1, a2, a3, a4, a5, a6) { +//│ let f9, g9; +//│ g9 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ let tmp, tmp1, tmp2, tmp3; -//│ tmp = a1 + a2; -//│ tmp1 = tmp + a3; -//│ tmp2 = tmp1 + a4; -//│ tmp3 = tmp2 + a5; -//│ return tmp3 + a6; +//│ let tmp1, tmp2, tmp3, tmp4; +//│ tmp1 = a1 + a2; +//│ tmp2 = tmp1 + a3; +//│ tmp3 = tmp2 + a4; +//│ tmp4 = tmp3 + a5; +//│ return tmp4 + a6; //│ }; //│ }; -//│ f7 = function f(a1, a2, a3, a4, a5, a6) { -//│ let tmp, g$this; -//│ g$this = g7(a1, a2, a3, a4, a5, a6) ?? null; -//│ tmp = g$this(); -//│ return tmp; +//│ f9 = function f(a1, a2, a3, a4, a5, a6) { +//│ let tmp1, g$this; +//│ g$this = g9(a1, a2, a3, a4, a5, a6) ?? null; +//│ tmp1 = g$this(); +//│ return tmp1; //│ }; -//│ f7(1, 2, 3, 4, 5, 6) +//│ f9(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' @@ -143,14 +182,14 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture1; -//│ g9 = function g(f$capture2) { +//│ let f11, Tuple1, g11, h3, ret, f12, f21, x, y, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, f$capture1; +//│ g11 = function g(f$capture2) { //│ return () => { //│ f$capture2.a0$ = 1; //│ return null; //│ }; //│ }; -//│ h1 = function h(f$capture2) { +//│ h3 = function h(f$capture2) { //│ return () => { //│ return f$capture2.a0$; //│ }; @@ -162,12 +201,12 @@ x + y //│ } //│ toString() { return "f$capture(" + this.a0$ + ")"; } //│ }; -//│ f9 = function f() { +//│ f11 = function f() { //│ let capture, g$this, h$this; //│ capture = new f$capture1(); //│ capture.a0$ = 0; -//│ g$this = g9(capture) ?? null; -//│ h$this = h1(capture) ?? null; +//│ g$this = g11(capture) ?? null; +//│ h$this = h3(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; //│ Tuple1 = function Tuple(a1, b1) { return new Tuple.class(a1, b1); }; @@ -178,17 +217,17 @@ x + y //│ } //│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } //│ }; -//│ tmp = f9(); -//│ ret = tmp; -//│ f11 = ret.a; +//│ tmp1 = f11(); +//│ ret = tmp1; +//│ f12 = ret.a; //│ f21 = ret.b; -//│ tmp1 = f21() ?? null; -//│ tmp2 = tmp1.toString() ?? null; -//│ x = tmp2; -//│ tmp3 = f11() ?? null; -//│ tmp4 = f21() ?? null; -//│ tmp5 = tmp4.toString() ?? null; -//│ y = tmp5; +//│ tmp2 = f21() ?? null; +//│ tmp3 = tmp2.toString() ?? null; +//│ x = tmp3; +//│ tmp4 = f12() ?? null; +//│ tmp5 = f21() ?? null; +//│ tmp6 = tmp5.toString() ?? null; +//│ y = tmp6; //│ x + y //│ = '01' //│ f1 = [Function (anonymous)] @@ -246,14 +285,14 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f14, g13, h5, f$capture5; -//│ g13 = function g(immutable, f$capture6) { +//│ let f16, g15, h7, f$capture5; +//│ g15 = function g(immutable, f$capture6) { //│ return () => { //│ f$capture6.mutated0$ = 2; //│ return immutable + f$capture6.mutated0$; //│ }; //│ }; -//│ h5 = function h(f$capture6) { +//│ h7 = function h(immutable, f$capture6) { //│ return () => { //│ return f$capture6.mutated0$; //│ }; @@ -265,18 +304,18 @@ f(1, 2, 1000) //│ } //│ toString() { return "f$capture(" + this.mutated0$ + ")"; } //│ }; -//│ f14 = function f(unused, immutable, mutated) { -//│ let a1, tmp21, tmp22, tmp23, capture, g$this, h$this; +//│ f16 = function f(unused, immutable, mutated) { +//│ let a1, tmp22, tmp23, tmp24, capture, g$this, h$this; //│ capture = new f$capture5(mutated); -//│ g$this = g13(immutable, capture) ?? null; -//│ tmp21 = g$this(); -//│ a1 = tmp21; -//│ h$this = h5(capture) ?? null; -//│ tmp22 = h$this(); -//│ tmp23 = a1 + tmp22; -//│ return tmp23 + unused; +//│ g$this = g15(immutable, capture) ?? null; +//│ tmp22 = g$this(); +//│ a1 = tmp22; +//│ h$this = h7(immutable, capture) ?? null; +//│ tmp23 = h$this(); +//│ tmp24 = a1 + tmp23; +//│ return tmp24 + unused; //│ }; -//│ f14(1, 2, 1000) +//│ f16(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -308,37 +347,88 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let f20, g19, h7; -//│ g19 = function g(arg) { +//│ let f23, g23, h9; +//│ g23 = function g(arg) { //│ return (n) => { -//│ let scrut, tmp21, h$this; +//│ let scrut, tmp22, h$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { -//│ tmp21 = n - 1; -//│ h$this = h7(arg) ?? null; -//│ return h$this(tmp21); +//│ tmp22 = n - 1; +//│ h$this = h9(arg) ?? null; +//│ return h$this(tmp22); //│ } //│ }; //│ }; -//│ h7 = function h(arg) { +//│ h9 = function h(arg) { //│ return (n) => { -//│ let scrut, tmp21, g$this; +//│ let scrut, tmp22, g$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { -//│ tmp21 = n - 1; -//│ g$this = g19(arg) ?? null; -//│ return g$this(tmp21); +//│ tmp22 = n - 1; +//│ g$this = g23(arg) ?? null; +//│ return g$this(tmp22); //│ } //│ }; //│ }; -//│ f20 = function f(arg) { +//│ f23 = function f(arg) { //│ let h$this; -//│ h$this = h7(arg) ?? null; +//│ h$this = h9(arg) ?? null; //│ return h$this(5); //│ }; -//│ f20(2) +//│ f23(2) //│ = 2 + +:expect 1 +fun f() = + let x = 1 + fun g() = x + fun f() = g() + f() +f() +//│ = 1 + +// here, g does not need the capture +:todo +:sjs +:expect 1 +fun f() = + let x = 1 + fun g() = 1 + fun f() = set x = 1 + f() + x +f() +//│ JS (unsanitized): +//│ let f30, f31, g27, f$capture9; +//│ g27 = function g(f$capture10) { +//│ return () => { +//│ return 1; +//│ }; +//│ }; +//│ f31 = function f(f$capture10) { +//│ return () => { +//│ f$capture10.x0$ = 1; +//│ return null; +//│ }; +//│ }; +//│ f$capture9 = function f$capture(x0$1) { return new f$capture.class(x0$1); }; +//│ f$capture9.class = class f$capture { +//│ constructor(x0$) { +//│ this.x0$ = x0$; +//│ } +//│ toString() { return "f$capture(" + this.x0$ + ")"; } +//│ }; +//│ f30 = function f() { +//│ let tmp22, capture, f$this; +//│ capture = new f$capture9(); +//│ capture.x0$ = 1; +//│ f$this = f31(capture) ?? null; +//│ tmp22 = f$this(); +//│ return capture.x0$; +//│ }; +//│ f30() +//│ = 1 From a12869df517016356bff0c1625be22cf40dab517 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 18:58:49 +0800 Subject: [PATCH 022/127] Fix ctor generation --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 238 ++++++++++-------- .../scala/hkmc2/codegen/js/JSBuilder.scala | 18 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 30 +++ .../src/test/mlscript/codegen/Lifter.mls | 25 +- 4 files changed, 199 insertions(+), 112 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 4a9499fce..7c2d23098 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -16,21 +16,30 @@ import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet import scala.annotation.nowarn -// Lifts classes and functions to the top-level. -// Assumes the input block does not have any `HandleBlock`s and lamdbas are -// rewritten as functions (lambdas will be removed from the IR soon). +/** + * Lifts classes and functions to the top-level. + * Assumes the input block does not have any `HandleBlock`s and lamdbas are + * rewritten as functions (lambdas will be removed from the IR soon). + */ class Lifter(using State): - // Describes the free variables of a function. - // vars: The free variables that are accessed or mutated by nested classes/functions. - // mutated: The free variables that are mutated, but not accessed, by nested classes/functions. + /** + * Describes the free variables of a function that have been accessed by nested definitions. + * @param vars The free variables that are accessed or mutated by nested classes/functions. + * @param mutated The free variables that are mutated, but not accessed, by nested classes/functions. + */ case class FreeVars(vars: List[Local], mutated: List[Local]) // use mutable sets locally to avoid reconstructing everything // linked hash sets preserve order - private case class FreeVarsMut(vars: LinkedHashSet[Local], mutated: LinkedHashSet[Local]) + private case class FreeVarsMut(vars: LinkedHashSet[Local], mutated: LinkedHashSet[Local]): + def toImmut = FreeVars(vars.toList, mutated.toList) - class UsedLocalsMap(mp: Map[BlockMemberSymbol, FreeVars]): + /** + * Describes the free variables of a function that have been accessed by nested definitions. + * @param mp The map from functions' `BlockMemberSymbol`s to their accessed variables. + */ + class UsedLocalsMap(val mp: Map[BlockMemberSymbol, FreeVars]): def apply(f: BlockMemberSymbol) = mp(f) private lazy val inverse = mp.flatMap: case fn -> vars => vars.vars.map(v => v -> fn) @@ -38,9 +47,9 @@ class Lifter(using State): def lookup(l: Local) = inverse.get(l) object UsedLocalsMap: - def from(mp: Map[FunDefn, FreeVars]) = + def from(mp: Map[BlockMemberSymbol, FreeVars]) = UsedLocalsMap(mp.map: - case a -> b => a.sym -> b + case a -> b => a -> b ) /** @@ -98,15 +107,24 @@ class Lifter(using State): (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: case s: FlowSymbol => s - // Given a function definition f and previously bound locals boundLocals, - // creates a map FunDefn -> List[Local] where each function definitions f - // in boundLocals is associated with a list of locals which both: - // - First occur in f, i.e. is a free variable of f - // - Are accessed by some definition within f - // These are the variables which will be moved to the capture. - // We do this once for every top-level function definition instead - // of once for every function definition so that we only traverse - // the tree once. + // + + /** + * Given a function definition `f` and previously bound locals `lookup`, + * creates a map `FunDefn` -> `List[Local]` where each function definition f + * in boundLocals is associated with a list of locals which both: + * - First occur in f, i.e. is a free variable of f, + * - Are accessed by some definition within f. + * + * These are the variables which will be moved to the capture. + * We do this once for every top-level function definition instead + * of once for every function definition so that we only traverse + * the tree once. + * + * @param f The function in which to find used locals. + * @param lookup The map describing which function a particular local was defined by. + * @return The used locals of `f` and all its nested definitions. + */ private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[FunDefn, FreeVarsMut] = val definedVars = getVars(f) @@ -173,16 +191,31 @@ class Lifter(using State): retMap.toMap + private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = + c.methods.map(findUsedLocalsImpl(_, Map.empty)) + .foldLeft(findUsedLocals(c.preCtor).mp ++ findUsedLocals(c.ctor).mp): + case (acc, newMap) => acc ++ newMap.map: + case defn -> freeVars => defn.sym -> freeVars.toImmut + + /** + * Finds the used locals of functions which have been used by their nested definitions. + * + * @param b + * @return + */ def findUsedLocals(b: Block): UsedLocalsMap = - var usedMap: Map[FunDefn, FreeVars] = Map.empty + var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(f: FunDefn, rest) => val m = findUsedLocalsImpl(f, Map.empty).map: case f -> FreeVarsMut(vars, mutated) => - f -> FreeVars(vars.toList, mutated.toList) + f.sym -> FreeVars(vars.toList, mutated.toList) usedMap ++= m super.applyBlock(b) + case Define(c: ClsLikeDefn, rest) => + usedMap ++= findUsedLocalsCls(c) + super.applyBlock(b) case _ => super.applyBlock(b) walker.applyBlock(b) UsedLocalsMap.from(usedMap) @@ -312,7 +345,7 @@ class Lifter(using State): val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym).filter: c => parentCls match - case Some(value) if d.isInstanceOf[FunDefn] => value != parentCls + case Some(value) if d.isInstanceOf[FunDefn] => value != value case _ => true .collect: // this line is just to satisfy the type system, in reality anything we capture is an InnerSymbol @@ -345,9 +378,86 @@ class Lifter(using State): case Define(f: FunDefn, rest) => ret = ret ++ createLiftInfoFn(f, N, ctx) super.applyBlock(b) + case Define(c: ClsLikeDefn, rest) => + ret = ret ++ createLiftInfoCls(c, ctx) + super.applyBlock(b) case _ => super.applyBlock(b) walker.applyBlock(b) ret + + def rewriteBlk(b: Block, ctx: LifterCtx): Block = + // replaces references to BlockMemberSymbols as needed with fresh variables, and + // returns the mapping from the symbol to the required variable. When possible, + // it also directly rewrites Results. + def rewriteBms(b: Block, ctx: LifterCtx) = + val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty + + val walker = new BlockTransformerNoRec(SymbolSubst()): + // only scan within the block. don't traverse + + // if possible, directly create the call and replace the result with it + override def applyResult(r: Result): Result = r match + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) + case _ => super.applyResult(r) + + // otherwise, there's no choice but to create the call earlier + override def applyValue(v: Value): Value = v match + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => + val newSym = syms.get(l) match + case None => + val newSym = FlowSymbol(l.nme + "$this") + syms.addOne(l -> newSym) + newSym + case Some(value) => value + Value.Ref(newSym) + case _ => super.applyValue(v) + (walker.applyBlock(b), syms.toList) + end rewriteBms + + // rewrites references to variables + val transformer1 = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + case None => super.applyBlock(b) + + case Assign(lhs, rhs, rest) => + ctx.getLocalCaptureSym(lhs) match + case Some(captureSym) => + AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) + case None => ctx.getLocalPath(lhs) match + case None => super.applyBlock(b) + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Path = p match + /* + case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match + case None => super.applyPath(p) + case Some(value) => value + */ + case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + case Value.Ref(l) => ctx.getLocalCaptureSym(l) match + case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) + case None => ctx.getLocalPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + case _ => super.applyPath(p) + + // rewrites references to block member symbols + val transformer2 = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = + val (rewriten, syms) = rewriteBms(b, ctx) + val pre = syms.foldLeft(blockBuilder): + case (blk, (bms, local)) => + blk.assign(local, createCall(bms, ctx)) + pre.rest(super.applyBlock(rewriten)) + + b |> transformer1.applyBlock |> transformer2.applyBlock + + def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = val info = ctx.getBmsReqdInfo(sym).get @@ -446,79 +556,6 @@ class Lifter(using State): Lifted(newDef, clsDefs) - def rewriteBlk(b: Block, ctx: LifterCtx): Block = - // replaces references to BlockMemberSymbols as needed with fresh variables, and - // returns the mapping from the symbol to the required variable. When possible, - // it also directly rewrites Results. - def rewriteBms(b: Block, ctx: LifterCtx) = - val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty - - val walker = new BlockTransformerNoRec(SymbolSubst()): - // only scan within the block. don't traverse - - // if possible, directly create the call and replace the result with it - override def applyResult(r: Result): Result = r match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) - case _ => super.applyResult(r) - - // otherwise, there's no choice but to create the call earlier - override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => - val newSym = syms.get(l) match - case None => - val newSym = FlowSymbol(l.nme + "$this") - syms.addOne(l -> newSym) - newSym - case Some(value) => value - Value.Ref(newSym) - case _ => super.applyValue(v) - (walker.applyBlock(b), syms.toList) - end rewriteBms - - // rewrites references to variables - val transformer1 = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match - case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - case None => super.applyBlock(b) - - case Assign(lhs, rhs, rest) => - ctx.getLocalCaptureSym(lhs) match - case Some(captureSym) => - AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) - case None => ctx.getLocalPath(lhs) match - case None => super.applyBlock(b) - case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - case _ => super.applyBlock(b) - - override def applyPath(p: Path): Path = p match - /* - case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match - case None => super.applyPath(p) - case Some(value) => value - */ - case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) - case Value.Ref(l) => ctx.getLocalCaptureSym(l) match - case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) - case None => ctx.getLocalPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) - case _ => super.applyPath(p) - - // rewrites references to block member symbols - val transformer2 = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = - val (rewriten, syms) = rewriteBms(b, ctx) - val pre = syms.foldLeft(blockBuilder): - case (blk, (bms, local)) => - blk.assign(local, createCall(bms, ctx)) - pre.rest(super.applyBlock(rewriten)) - - b |> transformer1.applyBlock |> transformer2.applyBlock - - def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) @@ -561,11 +598,14 @@ class Lifter(using State): def transform(b: Block) = val ctx = LifterCtx.withLocals(findUsedLocals(b)) val ctxx = ctx.addBmsReqdInfo(createLiftInfo(b, ctx)) - + val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match - case Define(f: FunDefn, rest) => - val Lifted(d, extra) = liftDefnsInFn(f, ctxx) - (d :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) + case Define(d, rest) => + val Lifted(lifted, extra) = d match + case f: FunDefn => liftDefnsInFn(f, ctxx) + case c: ClsLikeDefn => liftDefnsInCls(c, ctxx) + case _ => return super.applyBlock(b) + (lifted :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(b) \ No newline at end of file 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 e15edc428..5e9bd506d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -164,7 +164,7 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: doc"${getVar(sym)} = function ${sym.nme}($params) ${ braced(bodyDoc) };" case ClsLikeDefn(ownr, isym, sym, kind, paramsOpt, auxParams, par, mtds, privFlds, _pubFlds, preCtor, ctor) => // * Note: `_pubFlds` is not used because in JS, fields are not declared - val clsParams = paramsOpt.fold(Nil)(_.paramSyms) ++ auxParams.flatMap(_.paramSyms) + val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) val privs = val scp = isym.asInstanceOf[InnerSymbol].privatesScope @@ -176,11 +176,19 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: case (acc, (sym, nme)) => doc"$acc # this.${sym.name} = $nme;" val ctorCode = doc"$preCtorCode${body(ctor)}" + val ctorBraced = doc"${ braced(ctorCode)}" + + val pss = auxParams.map(setupFunction(N, _, End())._1) + val funBod = pss.foldRight(ctorBraced): + case (psDoc, doc) => doc"($psDoc) => \n$doc" + + val funBodBraced = if pss.isEmpty then funBod else doc"${braced(funBod)}" + val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ privs } # constructor(${ ctorParams.unzip._2.mkDocument(", ") - }) ${ braced(ctorCode) }${ + }) $funBodBraced${ mtds.map: case td @ FunDefn(_, _, ps :: pss, bod) => val result = pss.foldRight(bod): @@ -225,9 +233,9 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: case ps_ :: pss_ => val (ps, _) = setupFunction(some(sym.nme), ps_, End()) val pss = pss_.map(setupFunction(N, _, End())._1) - val paramsDoc = pss.foldLeft(ps): - case (doc, ps) => doc"${doc}, ${ps}" - val bod = doc"return new ${sym.nme}.class($paramsDoc);" + val paramsDoc = pss.foldLeft(doc"($ps)"): + case (doc, ps) => doc"${doc}(${ps})" + val bod = doc"return new ${sym.nme}.class$paramsDoc;" val funBod = pss.foldRight(bod): case (psDoc, doc) => doc"($psDoc) => ${braced(doc)}" S(doc"function ${sym.nme}($ps) { $funBod }") diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..ce8de7af7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -9,3 +9,33 @@ +:lift +:sjs +class A(a) with + fun f(b) = + fun g() = a + 2 +A(2).f(2) +//│ Elab: { Cls AParamList(‹›,List(Param(‹›,class:A‹151›.a,None)),None) { ‹› fun member:f‹149›(Param(‹›,b‹153›,None), ) = { ‹› fun member:g‹154›() = class:A‹151›.a#666; 2 }; }; member:A‹150›#666(2).f(2) } +//│ JS (unsanitized): +//│ let A1, tmp; +//│ A1 = function A(a1) { return new A.class(a1); }; +//│ A1.class = class A { +//│ constructor(a) { +//│ this.a = a; +//│ } +//│ f(b) { +//│ return 2; +//│ } +//│ g() { +//│ return this.a; +//│ } +//│ toString() { return "A(" + this.a + ")"; } +//│ }; +//│ tmp = A1(2); +//│ tmp.f(2) ?? null +//│ = 2 + +//│ Elab: { } +//│ JS (unsanitized): +//│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 1eadb7532..0b268a26c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -63,7 +63,6 @@ f(1, 2) //│ = 5 // don't touch g if not needed -// CURRENTLY NOT WORKING :todo :sjs fun f(used1, unused1) = @@ -87,6 +86,15 @@ f(1, 2) //│ f5(1, 2) //│ = 5 +:fixme +:sjs +class A(a) with + fun f() = + fun g() = a + g +A(2).f()() +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:g (class hkmc2.semantics.BlockMemberSymbol) + :todo :sjs fun f(used1, unused1) = @@ -96,7 +104,7 @@ fun f(used1, unused1) = fun h = used3 used1 + h let unused2 = 2 - class Test() with + class Test(a) with fun get() = used2 + used1 Test() f(1, 2).get() @@ -116,17 +124,18 @@ f(1, 2).get() //│ return used1 + tmp1; //│ }; //│ }; -//│ Test1 = function Test() { (used11, used21) => {return new Test.class(, used11, used21); +//│ Test1 = function Test(a1) { (used11, used21) => {return new Test.class(a1)(used11, used21); //│ } }; //│ Test1.class = class Test { -//│ constructor(used1, used2) { -//│ this.used1 = used1; -//│ this.used2 = used2; +//│ constructor(a) {(used1, used2) => +//│ { +//│ this.a = a; +//│ } //│ } //│ get() { //│ return this.used2 + this.used1; //│ } -//│ toString() { return "Test(" + this.used1 + ", " + this.used2 + ")"; } +//│ toString() { return "Test(" + this.a + ")"; } //│ }; //│ f7 = function f(used1, unused1) { //│ let used2, unused2, Test$this; @@ -391,7 +400,7 @@ fun f() = f() //│ = 1 -// here, g does not need the capture +// Here, g does not need the capture. This should be optimized :todo :sjs :expect 1 From 27f390fc640b77cb4a377201dd612ba88794c1b5 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 19:45:43 +0800 Subject: [PATCH 023/127] Fix ctor generation --- .../scala/hkmc2/codegen/js/JSBuilder.scala | 13 ++--- hkmc2/shared/src/test/mlscript/HkScratch.mls | 4 +- .../src/test/mlscript/bbml/bbCodeGen.mls | 4 +- .../test/mlscript/codegen/ClassInClass.mls | 8 ++- .../src/test/mlscript/codegen/ClassInFun.mls | 4 +- .../src/test/mlscript/codegen/FunInClass.mls | 20 +++++-- .../src/test/mlscript/codegen/Getters.mls | 4 +- .../src/test/mlscript/codegen/Hygiene.mls | 4 +- .../src/test/mlscript/codegen/Lifter.mls | 52 ++++++++++++------- .../src/test/mlscript/codegen/Modules.mls | 6 ++- .../test/mlscript/codegen/ParamClasses.mls | 16 ++++-- .../test/mlscript/codegen/SanityChecks.mls | 16 ++++-- .../src/test/mlscript/handlers/Effects.mls | 4 +- .../mlscript/handlers/EffectsInClasses.mls | 8 ++- .../mlscript/handlers/RecursiveHandlers.mls | 16 ++++-- .../test/mlscript/handlers/StackSafety.mls | 24 ++++++--- 16 files changed, 142 insertions(+), 61 deletions(-) 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 5e9bd506d..ccdbb4b93 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -176,13 +176,13 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: case (acc, (sym, nme)) => doc"$acc # this.${sym.name} = $nme;" val ctorCode = doc"$preCtorCode${body(ctor)}" - val ctorBraced = doc"${ braced(ctorCode)}" + val ctorBraced = doc"${ braced(ctorCode) }" val pss = auxParams.map(setupFunction(N, _, End())._1) val funBod = pss.foldRight(ctorBraced): - case (psDoc, doc) => doc"($psDoc) => \n$doc" + case (psDoc, doc) => doc"($psDoc) => $doc" - val funBodBraced = if pss.isEmpty then funBod else doc"${braced(funBod)}" + val funBodBraced = if pss.isEmpty then funBod else doc"${ braced(doc" # return $funBod") }" val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ privs @@ -235,10 +235,11 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: val pss = pss_.map(setupFunction(N, _, End())._1) val paramsDoc = pss.foldLeft(doc"($ps)"): case (doc, ps) => doc"${doc}(${ps})" - val bod = doc"return new ${sym.nme}.class$paramsDoc;" + val bod = braced(doc" # return new ${sym.nme}.class$paramsDoc;") val funBod = pss.foldRight(bod): - case (psDoc, doc) => doc"($psDoc) => ${braced(doc)}" - S(doc"function ${sym.nme}($ps) { $funBod }") + case (psDoc, doc_) => doc"($psDoc) => $doc_" + val funBodRet = if pss.isEmpty then funBod else braced(doc" # return $funBod") + S(doc"function ${sym.nme}($ps) ${ funBodRet }") case Nil => N /* diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index ce8de7af7..3c73acd01 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -19,7 +19,9 @@ A(2).f(2) //│ Elab: { Cls AParamList(‹›,List(Param(‹›,class:A‹151›.a,None)),None) { ‹› fun member:f‹149›(Param(‹›,b‹153›,None), ) = { ‹› fun member:g‹154›() = class:A‹151›.a#666; 2 }; }; member:A‹150›#666(2).f(2) } //│ JS (unsanitized): //│ let A1, tmp; -//│ A1 = function A(a1) { return new A.class(a1); }; +//│ A1 = function A(a1) { +//│ return new A.class(a1); +//│ }; //│ A1.class = class A { //│ constructor(a) { //│ this.a = a; diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index 2f1114c2c..3a4f42ab8 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -61,7 +61,9 @@ false class Foo(x: Int) //│ JS (unsanitized): //│ let Foo1; -//│ Foo1 = function Foo(x2) { return new Foo.class(x2); }; +//│ Foo1 = function Foo(x2) { +//│ return new Foo.class(x2); +//│ }; //│ Foo1.class = class Foo { //│ constructor(x1) { //│ this.x = x1; diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls index 2b48603e8..ce0e06e9d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls @@ -18,14 +18,18 @@ class Outer(a, b) with log(i.i1(1)) //│ JS (unsanitized): //│ let Outer1; -//│ Outer1 = function Outer(a1, b1) { return new Outer.class(a1, b1); }; +//│ Outer1 = function Outer(a1, b1) { +//│ return new Outer.class(a1, b1); +//│ }; //│ Outer1.class = class Outer { //│ constructor(a, b) { //│ this.a = a; //│ this.b = b; //│ let tmp, tmp1, tmp2; //│ const this$Outer = this; -//│ this.Inner = function Inner(c1) { return new Inner.class(c1); }; +//│ this.Inner = function Inner(c1) { +//│ return new Inner.class(c1); +//│ }; //│ this.Inner.class = class Inner { //│ constructor(c) { //│ this.c = c; diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls index e9e73c178..08643bfd8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls @@ -40,7 +40,9 @@ fun test(x) = //│ let test5; //│ test5 = function test(x) { //│ let Foo, tmp; -//│ Foo = function Foo(a1, b1) { return new Foo.class(a1, b1); }; +//│ Foo = function Foo(a1, b1) { +//│ return new Foo.class(a1, b1); +//│ }; //│ Foo.class = class Foo { //│ constructor(a, b) { //│ this.a = a; diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls index 33f3d560a..1f8a53e7e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls @@ -25,7 +25,9 @@ fun test(a) = //│ let test3; //│ test3 = function test(a) { //│ let Inner; -//│ Inner = function Inner(b1) { return new Inner.class(b1); }; +//│ Inner = function Inner(b1) { +//│ return new Inner.class(b1); +//│ }; //│ Inner.class = class Inner { //│ constructor(b) { //│ this.b = b; @@ -80,7 +82,9 @@ fun test(a) = //│ let test5; //│ test5 = function test(a) { //│ let C1, C2, tmp1, tmp2; -//│ C1 = function C1(b1) { return new C1.class(b1); }; +//│ C1 = function C1(b1) { +//│ return new C1.class(b1); +//│ }; //│ C1.class = class C1 { //│ constructor(b) { //│ this.b = b; @@ -91,7 +95,9 @@ fun test(a) = //│ } //│ toString() { return "C1(" + this.b + ")"; } //│ }; -//│ C2 = function C2(b1) { return new C2.class(b1); }; +//│ C2 = function C2(b1) { +//│ return new C2.class(b1); +//│ }; //│ C2.class = class C2 { //│ constructor(b) { //│ this.b = b; @@ -128,7 +134,9 @@ class Foo(a) with Foo(123) //│ JS (unsanitized): //│ let Foo1; -//│ Foo1 = function Foo(a1) { return new Foo.class(a1); }; +//│ Foo1 = function Foo(a1) { +//│ return new Foo.class(a1); +//│ }; //│ Foo1.class = class Foo { //│ constructor(a) { //│ this.a = a; @@ -161,7 +169,9 @@ class Bar(x) with Bar(1) //│ JS (unsanitized): //│ let Bar1; -//│ Bar1 = function Bar(x1) { return new Bar.class(x1); }; +//│ Bar1 = function Bar(x1) { +//│ return new Bar.class(x1); +//│ }; //│ Bar1.class = class Bar { //│ constructor(x) { //│ this.x = x; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index d9d5ef835..e1076443d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -293,7 +293,9 @@ class Foo(x) with fun oops = x //│ JS (unsanitized): //│ let Foo1; -//│ Foo1 = function Foo(x1) { return new Foo.class(x1); }; +//│ Foo1 = function Foo(x1) { +//│ return new Foo.class(x1); +//│ }; //│ Foo1.class = class Foo { //│ constructor(x) { //│ this.x = x; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 87a909f6f..8dc3d181a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -99,7 +99,9 @@ class Cls(x) with print(this.x, x) //│ JS (unsanitized): //│ let Cls1; -//│ Cls1 = function Cls(x3) { return new Cls.class(x3); }; +//│ Cls1 = function Cls(x3) { +//│ return new Cls.class(x3); +//│ }; //│ Cls1.class = class Cls { //│ #x; //│ #x1; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 0b268a26c..78678a215 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -98,55 +98,59 @@ A(2).f()() :todo :sjs fun f(used1, unused1) = - let used2 = unused1 fun g(g_arg) = let used3 = 2 fun h = used3 used1 + h let unused2 = 2 class Test(a) with - fun get() = used2 + used1 - Test() + fun get() = used1 + Test(unused1) f(1, 2).get() //│ JS (unsanitized): //│ let f7, Test1, g7, h1, tmp; -//│ h1 = function h(used3, used1, used2) { +//│ h1 = function h(used3, used1) { //│ return () => { //│ return used3; //│ }; //│ }; -//│ g7 = function g(used1, used2) { +//│ g7 = function g(used1) { //│ return (g$_arg) => { //│ let used3, tmp1, h$this; //│ used3 = 2; -//│ h$this = h1(used3, used1, used2) ?? null; +//│ h$this = h1(used3, used1) ?? null; //│ tmp1 = h$this(); //│ return used1 + tmp1; //│ }; //│ }; -//│ Test1 = function Test(a1) { (used11, used21) => {return new Test.class(a1)(used11, used21); -//│ } }; +//│ Test1 = function Test(a1) { +//│ return (used11) => { +//│ return new Test.class(a1)(used11); +//│ } +//│ }; //│ Test1.class = class Test { -//│ constructor(a) {(used1, used2) => -//│ { +//│ constructor(a) { +//│ return (used1) => { //│ this.a = a; //│ } //│ } //│ get() { -//│ return this.used2 + this.used1; +//│ return this.used1; //│ } //│ toString() { return "Test(" + this.a + ")"; } //│ }; //│ f7 = function f(used1, unused1) { -//│ let used2, unused2, Test$this; -//│ used2 = unused1; +//│ let unused2, Test$this; //│ unused2 = 2; -//│ Test$this = Test1(used1, used2) ?? null; -//│ return Test$this(); +//│ Test$this = Test1(used1) ?? null; +//│ return Test$this(unused1); //│ }; //│ tmp = f7(1, 2); //│ tmp.get() ?? null -//│ ═══[RUNTIME ERROR] TypeError: Test$this is not a function +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'get') + //│ (used11) => +//│ JS (unsanitized): +//│ null // preserve order :sjs @@ -203,7 +207,9 @@ x + y //│ return f$capture2.a0$; //│ }; //│ }; -//│ f$capture1 = function f$capture(a0$1) { return new f$capture.class(a0$1); }; +//│ f$capture1 = function f$capture(a0$1) { +//│ return new f$capture.class(a0$1); +//│ }; //│ f$capture1.class = class f$capture { //│ constructor(a0$) { //│ this.a0$ = a0$; @@ -218,7 +224,9 @@ x + y //│ h$this = h3(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; -//│ Tuple1 = function Tuple(a1, b1) { return new Tuple.class(a1, b1); }; +//│ Tuple1 = function Tuple(a1, b1) { +//│ return new Tuple.class(a1, b1); +//│ }; //│ Tuple1.class = class Tuple { //│ constructor(a, b) { //│ this.a = a; @@ -306,7 +314,9 @@ f(1, 2, 1000) //│ return f$capture6.mutated0$; //│ }; //│ }; -//│ f$capture5 = function f$capture(mutated0$1) { return new f$capture.class(mutated0$1); }; +//│ f$capture5 = function f$capture(mutated0$1) { +//│ return new f$capture.class(mutated0$1); +//│ }; //│ f$capture5.class = class f$capture { //│ constructor(mutated0$) { //│ this.mutated0$ = mutated0$; @@ -424,7 +434,9 @@ f() //│ return null; //│ }; //│ }; -//│ f$capture9 = function f$capture(x0$1) { return new f$capture.class(x0$1); }; +//│ f$capture9 = function f$capture(x0$1) { +//│ return new f$capture.class(x0$1); +//│ }; //│ f$capture9.class = class f$capture { //│ constructor(x0$) { //│ this.x0$ = x0$; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index cdb2089ed..361a2bb09 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -50,7 +50,9 @@ module M with //│ constructor() {} //│ toString() { return "C"; } //│ }; -//│ this.D = function D() { return new D.class(); }; +//│ this.D = function D() { +//│ return new D.class(); +//│ }; //│ this.D.class = class D { //│ constructor() {} //│ toString() { return "D(" + + ")"; } @@ -83,7 +85,7 @@ M.y :re M.oops //│ ╔══[ERROR] Module 'M' does not contain member 'oops' -//│ ║ l.84: M.oops +//│ ║ l.87: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls index 6051433ee..e4c979c0f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls @@ -10,7 +10,9 @@ class Foo() //│ JS (unsanitized): //│ let Foo1; -//│ Foo1 = function Foo() { return new Foo.class(); }; +//│ Foo1 = function Foo() { +//│ return new Foo.class(); +//│ }; //│ Foo1.class = class Foo { //│ constructor() {} //│ toString() { return "Foo(" + + ")"; } @@ -36,7 +38,9 @@ Foo.class class Foo(a) //│ JS (unsanitized): //│ let Foo3; -//│ Foo3 = function Foo(a1) { return new Foo.class(a1); }; +//│ Foo3 = function Foo(a1) { +//│ return new Foo.class(a1); +//│ }; //│ Foo3.class = class Foo { //│ constructor(a) { //│ this.a = a; @@ -70,7 +74,9 @@ foo(27) class Foo(a, b) //│ JS (unsanitized): //│ let Foo5; -//│ Foo5 = function Foo(a1, b1) { return new Foo.class(a1, b1); }; +//│ Foo5 = function Foo(a1, b1) { +//│ return new Foo.class(a1, b1); +//│ }; //│ Foo5.class = class Foo { //│ constructor(a, b) { //│ this.a = a; @@ -133,7 +139,9 @@ class Inner(c) with log(c) //│ JS (unsanitized): //│ let Inner1; -//│ Inner1 = function Inner(c1) { return new Inner.class(c1); }; +//│ Inner1 = function Inner(c1) { +//│ return new Inner.class(c1); +//│ }; //│ Inner1.class = class Inner { //│ constructor(c) { //│ this.c = c; diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index 0eb1906ac..c196d98bf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -86,7 +86,9 @@ class Cls(x, y) with fun f(z, p) = x + y + z + p Cls(1, 2).f(3) //│ JS: -//│ Cls1 = function Cls(x1, y1) { return new Cls.class(x1, y1); }; +//│ Cls1 = function Cls(x1, y1) { +//│ return new Cls.class(x1, y1); +//│ }; //│ Cls1.class = class Cls { //│ constructor(x, y) { //│ this.x = x; @@ -110,7 +112,9 @@ class Cls(x, y) with fun f(z, p) = x + y + z + p Cls(1, 2).f(3) //│ JS: -//│ Cls3 = function Cls(...args1) { return new Cls.class(...args1); }; +//│ Cls3 = function Cls(...args1) { +//│ return new Cls.class(...args1); +//│ }; //│ Cls3.class = class Cls { //│ constructor(x, y) { //│ this.x = x; @@ -138,7 +142,9 @@ class Cls(x, y) with fun f(z, p)(q, s) = x + y + z + p + q + s Cls(1, 2).f(3, 4)(5) //│ JS: -//│ Cls5 = function Cls(...args1) { return new Cls.class(...args1); }; +//│ Cls5 = function Cls(...args1) { +//│ return new Cls.class(...args1); +//│ }; //│ Cls5.class = class Cls { //│ constructor(x, y) { //│ this.x = x; @@ -214,7 +220,9 @@ if M.A(1).y is //│ JS: //│ const M$class = class M { //│ constructor() { -//│ this.A = function A(...args1) { return new A.class(...args1); }; +//│ this.A = function A(...args1) { +//│ return new A.class(...args1); +//│ }; //│ this.A.class = class A { //│ constructor(x1) { //│ this.x = x1; diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index d21f30665..89a9cd222 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -172,7 +172,9 @@ if true do //│ toString() { return "Effect$h$"; } //│ }; //│ h = new Effect$h$(); -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp13; diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index 537b5f5c0..9b2799acc 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -11,13 +11,17 @@ class Lol(h) with print(h.perform("k")) //│ JS (unsanitized): //│ let Lol1; -//│ Lol1 = function Lol(h1) { return new Lol.class(h1); }; +//│ Lol1 = function Lol(h1) { +//│ return new Lol.class(h1); +//│ }; //│ Lol1.class = class Lol { //│ constructor(h) { //│ this.h = h; //│ let tmp, res, res1, Cont$; //│ const this$Lol = this; -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp1; diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 4b4895119..fc79230c6 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -135,7 +135,9 @@ str //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(h1, (k) => { //│ let tmp9, tmp10, tmp11, res5, Cont$1; -//│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$1 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp12; @@ -175,7 +177,9 @@ str //│ toString() { return "Effect$h1$"; } //│ }; //│ h1 = new Effect$h1$(); -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp9; @@ -211,7 +215,9 @@ str //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(h2, (k) => { //│ let tmp10, tmp11, tmp12, tmp13, tmp14, res7, Cont$2; -//│ Cont$2 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$2 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$2.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp15; @@ -254,7 +260,9 @@ str //│ toString() { return "Effect$h2$"; } //│ }; //│ h2 = new Effect$h2$(); -//│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$1 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp10; diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index e7ead6ded..b62abd398 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -23,7 +23,9 @@ hi(0) //│ let hi1, res, handleBlock$1; //│ hi1 = function hi(n) { //│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, res1, Cont$; -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp1; @@ -90,7 +92,9 @@ hi(0) //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { //│ let res2, res3, Cont$1; -//│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$1 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; @@ -125,7 +129,9 @@ hi(0) //│ toString() { return "StackDelay$"; } //│ }; //│ stackHandler = new StackDelay$(); -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; @@ -178,7 +184,9 @@ sum(10000) //│ let sum3, res1, handleBlock$3; //│ sum3 = function sum(n) { //│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$; -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp3; @@ -282,7 +290,9 @@ sum(10000) //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { //│ let res3, res4, Cont$1; -//│ Cont$1 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$1 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; @@ -317,7 +327,9 @@ sum(10000) //│ toString() { return "StackDelay$"; } //│ }; //│ stackHandler = new StackDelay$(); -//│ Cont$ = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$ = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; From 398275607c196091f36a9d151f796f2e19d8b41f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 21:39:14 +0800 Subject: [PATCH 024/127] classes almost working, refactor --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 119 ++++++++++-------- .../scala/hkmc2/codegen/js/JSBuilder.scala | 17 ++- hkmc2/shared/src/test/mlscript/HkScratch.mls | 32 ----- .../src/test/mlscript/codegen/Lifter.mls | 100 ++++++++++++--- .../src/test/mlscript/codegen/Modules.mls | 2 +- 5 files changed, 160 insertions(+), 110 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 7c2d23098..2bf063558 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -73,7 +73,7 @@ class Lifter(using State): val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures val bmsPaths: Map[BlockMemberSymbol, Path], val localPaths: Map[Local, Local], - val iSymPaths: Map[InnerSymbol, Local] + val isymPaths: Map[InnerSymbol, Local] ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -85,7 +85,7 @@ class Lifter(using State): def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) - def getIsymPath(l: InnerSymbol) = iSymPaths.get(l) + def getIsymPath(l: InnerSymbol) = isymPaths.get(l) def addFnDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) @@ -95,8 +95,9 @@ class Lifter(using State): def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) - def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(iSymPaths = m) + def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(isymPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) + def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, @@ -125,7 +126,7 @@ class Lifter(using State): * @param lookup The map describing which function a particular local was defined by. * @return The used locals of `f` and all its nested definitions. */ - private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[FunDefn, FreeVarsMut] = + private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[BlockMemberSymbol, FreeVarsMut] = val definedVars = getVars(f) // add this function's locals to the lookup map @@ -134,15 +135,15 @@ class Lifter(using State): val lookupNext = definedVars.map(_ -> f).toMap ++ lookup // collect all function definitions - val retMap: MutMap[FunDefn, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> f => f -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty) + val retMap: MutMap[BlockMemberSymbol, FreeVarsMut] = MutMap.from(lookupNext.map: + case _ -> f => f.sym -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty) ) // add this function in case this function has no locals - if !retMap.contains(f) then retMap.addOne(f -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty)) + if !retMap.contains(f.sym) then retMap.addOne(f.sym -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty)) // merge recursive call results - def merge(next: Map[FunDefn, FreeVarsMut]) = + def merge(next: Map[BlockMemberSymbol, FreeVarsMut]) = for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match case None => retMap.addOne(f -> v) case Some(value) => @@ -155,8 +156,8 @@ class Lifter(using State): def addLocal(l: Local, mut: Bool) = lookup.get(l) match case Some(f) => - if mut then retMap(f).mutated.addOne(l) - retMap(f).vars.addOne(l) + if mut then retMap(f.sym).mutated.addOne(l) + retMap(f.sym).vars.addOne(l) case None => if mut then if assignedOnce.contains(l) then assignedTwice.add(l) @@ -187,7 +188,7 @@ class Lifter(using State): walker.applyBlock(f.body) // add mutable locals - retMap(f).mutated ++= retMap(f).vars.intersect(assignedTwice) + retMap(f.sym).mutated ++= retMap(f.sym).vars.intersect(assignedTwice) retMap.toMap @@ -195,7 +196,7 @@ class Lifter(using State): c.methods.map(findUsedLocalsImpl(_, Map.empty)) .foldLeft(findUsedLocals(c.preCtor).mp ++ findUsedLocals(c.ctor).mp): case (acc, newMap) => acc ++ newMap.map: - case defn -> freeVars => defn.sym -> freeVars.toImmut + case defn -> freeVars => defn -> freeVars.toImmut /** * Finds the used locals of functions which have been used by their nested definitions. @@ -210,7 +211,7 @@ class Lifter(using State): case Define(f: FunDefn, rest) => val m = findUsedLocalsImpl(f, Map.empty).map: case f -> FreeVarsMut(vars, mutated) => - f.sym -> FreeVars(vars.toList, mutated.toList) + f -> FreeVars(vars.toList, mutated.toList) usedMap ++= m super.applyBlock(b) case Define(c: ClsLikeDefn, rest) => @@ -256,20 +257,26 @@ class Lifter(using State): (defn, varsMap, varsList) - private val clsLikeCache: MutMap[Defn, Set[Local]] = MutMap.empty + 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) = clsLikeCache.get(c) match + def getInnerSymbols(c: Defn) = + val sym = c match + case f: FunDefn => f.sym + case c: ClsLikeDefn => c.isym + case _ => c.sym // unreachable + + innerSymCache.get(sym) match case Some(value) => value case None => val ret: Set[Local] = c.freeVars.collect: case s: InnerSymbol => s case t: TermSymbol if t.owner.isDefined => t.owner.get - clsLikeCache.addOne(c -> ret) + innerSymCache.addOne(sym -> ret) ret /** @@ -343,13 +350,7 @@ class Lifter(using State): val FreeVars(vars, mut) = ctx.usedLocals(f.sym) vars.filter(!mut.contains(_)) - val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym).filter: c => - parentCls match - case Some(value) if d.isInstanceOf[FunDefn] => value != value - case _ => true - .collect: - // this line is just to satisfy the type system, in reality anything we capture is an InnerSymbol - case s: InnerSymbol => s + val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) @@ -363,12 +364,12 @@ class Lifter(using State): def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOutDefns - defns.flatMap(createLiftInfoCont(_, parentCls, ctx.addFnDefn(f))).toMap + defns.flatMap(createLiftInfoCont(_, N, ctx.addFnDefn(f))).toMap def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = c.preCtor.floatOutDefns._2 ++ c.ctor.floatOutDefns._2 val newCtx = ctx.addClsDefn(c) - defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap + defns.flatMap(f => createLiftInfoCont(f, N, newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, S(c), newCtx)) def createLiftInfo(b: Block, ctx: LifterCtx) = @@ -385,7 +386,7 @@ class Lifter(using State): walker.applyBlock(b) ret - def rewriteBlk(b: Block, ctx: LifterCtx): Block = + def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, // it also directly rewrites Results. @@ -414,15 +415,26 @@ class Lifter(using State): (walker.applyBlock(b), syms.toList) end rewriteBms + def belongsToCtor(l: Symbol) = + ctorCls.match + case None => false + case Some(value) => + value.isym === l + // rewrites references to variables val transformer1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match - case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - case None => super.applyBlock(b) + case Some(value) if !belongsToCtor(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + case _ => super.applyBlock(b) + + case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(value) => + AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) + case _ => super.applyBlock(b) - case Assign(lhs, rhs, rest) => - ctx.getLocalCaptureSym(lhs) match + case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match case Some(captureSym) => AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) case None => ctx.getLocalPath(lhs) match @@ -437,8 +449,12 @@ class Lifter(using State): case Some(value) => value */ case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) + case Some(value) if !belongsToCtor(value) => Value.Ref(value) + case _ => super.applyPath(p) + case Value.Ref(t: TermSymbol) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(value) => Select(value.asPath, t.id)(N) + case _ => super.applyPath(p) case Value.Ref(l) => ctx.getLocalCaptureSym(l) match case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) case None => ctx.getLocalPath(l) match @@ -463,7 +479,8 @@ class Lifter(using State): val info = ctx.getBmsReqdInfo(sym).get val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) - Call(sym.asPath, localsArgs ++ capturesArgs)(false) + val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) + Call(sym.asPath, iSymArgs ++ localsArgs ++ capturesArgs)(false) // deals with creating parameter lists def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match @@ -524,37 +541,35 @@ class Lifter(using State): val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns val (ctor, ctorDefns) = c.ctor.floatOutDefns - val newCtx = ctx // TODO: add block member symbol replacement + val newCtx = ctx.addIsymPath(c.isym, c.isym) + // TODO: add block member symbol replacement - val newPreCtor = rewriteBlk(preCtor, newCtx) - val newCtor = rewriteBlk(ctor, newCtx) + val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) + val newCtor = rewriteBlk(ctor, S(c), newCtx) val ctorDefnsLifted = (preCtorDefns ++ ctorDefns).flatMap: defn => val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) liftedDefn :: extraDefns - val fLifted = c.methods.flatMap: f => - val Lifted(liftedDefn, extraDefns) = liftDefnsInFn(f, newCtx) - liftedDefn :: extraDefns - - val allDefs = (ctorDefnsLifted ++ fLifted).map: - case f: FunDefn => f.copy(owner = S(c.isym)) + val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) + val methods = fLifted.collect: + // the type check FunDefn here does nothing, this is just to satisfy the tye system + case Lifted(liftedDefn: FunDefn, extraDefns) => liftedDefn + val fExtra = fLifted.flatMap: + case Lifted(liftedDefn, extraDefns) => extraDefns + + val extras = (ctorDefnsLifted ++ fExtra).map: + case f: FunDefn => f.copy(owner = c.owner) case c: ClsLikeDefn => c.copy(owner = c.owner) case d => d - - val funDefs = allDefs.collect: - case f: FunDefn => f - - val clsDefs = allDefs.collect: - case c: ClsLikeDefn => c - + val newDef = c.copy( - methods = funDefs, + methods = methods, preCtor = newPreCtor, ctor = newCtor ) - Lifted(newDef, clsDefs) + Lifted(newDef, extras) def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) @@ -578,7 +593,7 @@ class Lifter(using State): val newCtx = captureCtx .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) - val transformed = rewriteBlk(blk, newCtx) + val transformed = rewriteBlk(blk, N, newCtx) if thisVars.mutated.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) 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 ccdbb4b93..b21a46eba 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -176,19 +176,24 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: case (acc, (sym, nme)) => doc"$acc # this.${sym.name} = $nme;" val ctorCode = doc"$preCtorCode${body(ctor)}" - val ctorBraced = doc"${ braced(ctorCode) }" - val pss = auxParams.map(setupFunction(N, _, End())._1) - val funBod = pss.foldRight(ctorBraced): - case (psDoc, doc) => doc"($psDoc) => $doc" + val ctorBod = if auxParams.isEmpty then + doc"${braced(ctorCode)}" + else + + val pss = auxParams.map(setupFunction(N, _, End())._1) + val newCtorCode = doc"$ctorCode; # return this;" + val ctorBraced = doc"${ braced(newCtorCode) }" + val funBod = pss.foldRight(ctorBraced): + case (psDoc, doc) => doc"($psDoc) => $doc" - val funBodBraced = if pss.isEmpty then funBod else doc"${ braced(doc" # return $funBod") }" + doc"${ braced(doc" # return $funBod") }" val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ privs } # constructor(${ ctorParams.unzip._2.mkDocument(", ") - }) $funBodBraced${ + }) $ctorBod${ mtds.map: case td @ FunDefn(_, _, ps :: pss, bod) => val result = pss.foldRight(bod): diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 3c73acd01..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -9,35 +9,3 @@ -:lift -:sjs -class A(a) with - fun f(b) = - fun g() = a - 2 -A(2).f(2) -//│ Elab: { Cls AParamList(‹›,List(Param(‹›,class:A‹151›.a,None)),None) { ‹› fun member:f‹149›(Param(‹›,b‹153›,None), ) = { ‹› fun member:g‹154›() = class:A‹151›.a#666; 2 }; }; member:A‹150›#666(2).f(2) } -//│ JS (unsanitized): -//│ let A1, tmp; -//│ A1 = function A(a1) { -//│ return new A.class(a1); -//│ }; -//│ A1.class = class A { -//│ constructor(a) { -//│ this.a = a; -//│ } -//│ f(b) { -//│ return 2; -//│ } -//│ g() { -//│ return this.a; -//│ } -//│ toString() { return "A(" + this.a + ")"; } -//│ }; -//│ tmp = A1(2); -//│ tmp.f(2) ?? null -//│ = 2 - -//│ Elab: { } -//│ JS (unsanitized): -//│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 78678a215..fc1b040f3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -86,14 +86,7 @@ f(1, 2) //│ f5(1, 2) //│ = 5 -:fixme -:sjs -class A(a) with - fun f() = - fun g() = a - g -A(2).f()() -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:g (class hkmc2.semantics.BlockMemberSymbol) + :todo :sjs @@ -131,7 +124,8 @@ f(1, 2).get() //│ Test1.class = class Test { //│ constructor(a) { //│ return (used1) => { -//│ this.a = a; +//│ this.a = a;; +//│ return this; //│ } //│ } //│ get() { @@ -147,7 +141,6 @@ f(1, 2).get() //│ }; //│ tmp = f7(1, 2); //│ tmp.get() ?? null -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'get') //│ (used11) => //│ JS (unsanitized): //│ null @@ -410,6 +403,54 @@ fun f() = f() //│ = 1 +/// CLASSES /// + +:sjs +class A(a) with + fun f(b) = + fun g(c) = + set b = 2 + a + b + c + g +A(1).f(1)(3) +//│ JS (unsanitized): +//│ let A1, g27, tmp22, tmp23, f$capture9; +//│ g27 = function g(A$instance, f$capture10) { +//│ return (c1) => { +//│ let tmp24; +//│ f$capture10.b0$ = 2; +//│ tmp24 = A$instance.a + f$capture10.b0$; +//│ return tmp24 + c1; +//│ }; +//│ }; +//│ f$capture9 = function f$capture(b0$1) { +//│ return new f$capture.class(b0$1); +//│ }; +//│ f$capture9.class = class f$capture { +//│ constructor(b0$) { +//│ this.b0$ = b0$; +//│ } +//│ toString() { return "f$capture(" + this.b0$ + ")"; } +//│ }; +//│ A1 = function A(a2) { +//│ return new A.class(a2); +//│ }; +//│ A1.class = class A { +//│ constructor(a1) { +//│ this.a = a1; +//│ } +//│ f(b1) { +//│ let capture; +//│ capture = new f$capture9(b1); +//│ return g27(this, capture) ?? null; +//│ } +//│ toString() { return "A(" + this.a + ")"; } +//│ }; +//│ tmp22 = A1(1); +//│ tmp23 = tmp22.f(1) ?? null; +//│ tmp23(3) ?? null +//│ = 6 + // Here, g does not need the capture. This should be optimized :todo :sjs @@ -422,34 +463,55 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f30, f31, g27, f$capture9; -//│ g27 = function g(f$capture10) { +//│ let f30, f31, g29, f$capture11; +//│ g29 = function g(f$capture12) { //│ return () => { //│ return 1; //│ }; //│ }; -//│ f31 = function f(f$capture10) { +//│ f31 = function f(f$capture12) { //│ return () => { -//│ f$capture10.x0$ = 1; +//│ f$capture12.x0$ = 1; //│ return null; //│ }; //│ }; -//│ f$capture9 = function f$capture(x0$1) { +//│ f$capture11 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); //│ }; -//│ f$capture9.class = class f$capture { +//│ f$capture11.class = class f$capture { //│ constructor(x0$) { //│ this.x0$ = x0$; //│ } //│ toString() { return "f$capture(" + this.x0$ + ")"; } //│ }; //│ f30 = function f() { -//│ let tmp22, capture, f$this; -//│ capture = new f$capture9(); +//│ let tmp24, capture, f$this; +//│ capture = new f$capture11(); //│ capture.x0$ = 1; //│ f$this = f31(capture) ?? null; -//│ tmp22 = f$this(); +//│ tmp24 = f$this(); //│ return capture.x0$; //│ }; //│ f30() //│ = 1 + +// don't break private variables +:lift +:re +fun foo = () +class A(a) with + foo + let x = 2 + foo +A(2).x +//│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' + +:fixme +:expect 2 +class A(a) with + let x = 2 + fun f() = + fun g() = x + g() +A(2).f() +//│ ═══[RUNTIME ERROR] Expected: 2, got: null diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index 361a2bb09..a34ad67ec 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -85,7 +85,7 @@ M.y :re M.oops //│ ╔══[ERROR] Module 'M' does not contain member 'oops' -//│ ║ l.87: M.oops +//│ ║ l.86: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' From eb63e8c0c21d66fce4f6e0a265a13b9d548911b2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 21:40:15 +0800 Subject: [PATCH 025/127] revert weird whitespace change --- hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala index 747089e08..5cbe5e4e3 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala @@ -52,4 +52,5 @@ class CompileTestRunner compiler.report.badLines.distinct.sorted .map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) -end CompileTestRunner \ No newline at end of file +end CompileTestRunner + From b7f3e4182019e2552f44ef5a8362f00590b36a0c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 29 Jan 2025 23:50:21 +0800 Subject: [PATCH 026/127] can now lift classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 83 ++++- .../scala/hkmc2/codegen/js/JSBuilder.scala | 8 +- .../src/test/mlscript/codegen/Lifter.mls | 308 ++++++++++-------- 3 files changed, 236 insertions(+), 163 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 2bf063558..913bedaec 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -14,7 +14,6 @@ import scala.collection.mutable.LinkedHashSet import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet -import scala.annotation.nowarn /** * Lifts classes and functions to the top-level. @@ -107,8 +106,6 @@ class Lifter(using State): def getVars(f: FunDefn): Set[Local] = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: case s: FlowSymbol => s - - // /** * Given a function definition `f` and previously bound locals `lookup`, @@ -316,7 +313,8 @@ class Lifter(using State): case class LiftedInfo( val reqdCaptures: List[BlockMemberSymbol], val reqdVars: List[Local], - val reqdInnerSyms: List[InnerSymbol] + val reqdInnerSyms: List[InnerSymbol], + val fakeCtorBms: Option[BlockMemberSymbol] // only for classes ) case class Lifted( @@ -352,7 +350,11 @@ class Lifter(using State): val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) - val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) + val fakeCtorBms = d match + case c: ClsLikeDefn => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) + case _ => N + + val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms) if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then Map.empty else d match @@ -480,12 +482,17 @@ class Lifter(using State): val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) - Call(sym.asPath, iSymArgs ++ localsArgs ++ capturesArgs)(false) + + val callSym = info.fakeCtorBms match + case Some(v) => v + case None => sym + + Call(callSym.asPath, iSymArgs ++ localsArgs ++ capturesArgs)(false) // deals with creating parameter lists def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match case N => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms)) => val createSym = d match case d: ClsLikeDefn => ((nme: String) => TermSymbol(syntax.ParamBind, S(d.isym), Tree.Ident(nme))) case _ => ((nme: String) => VarSymbol(Tree.Ident(nme))) @@ -525,17 +532,57 @@ class Lifter(using State): .replIsymPaths(newIsymPaths) d match - case f: FunDefn => - val newDef = FunDefn( - base.owner, f.sym, PlainParamList(extraParams) :: f.params, f.body - ) - liftDefnsInFn(newDef, newCtx) - case c: ClsLikeDefn => - val newDef = c.copy( - owner = base.owner, auxParams = c.auxParams.appended(PlainParamList(extraParams)) - ) - liftDefnsInCls(newDef, newCtx) - case _ => Lifted(d, Nil) + case f: FunDefn => + val newDef = FunDefn( + base.owner, f.sym, PlainParamList(extraParams) :: f.params, f.body + ) + liftDefnsInFn(newDef, newCtx) + case c: ClsLikeDefn => + val newDef = c.copy( + owner = base.owner, auxParams = c.auxParams.appended(PlainParamList(extraParams)) + ) + val Lifted(lifted, extras) = liftDefnsInCls(newDef, newCtx) + + fakeCtorBms match + case None => Lifted(lifted, extras) // unreachable + case Some(bms) => + // create the fake ctor here + + inline def mapParams(ps: ParamList) = ps.params.map(p => VarSymbol(p.sym.id)) + + val paramSyms = c.paramsOpt.map(mapParams) + val auxSyms = c.auxParams.map(mapParams) + val extraSyms = extraParams.map(p => VarSymbol(p.sym.id)) + + val paramArgs = paramSyms.getOrElse(Nil).map(_.asPath) + + inline def toPaths(l: List[Local]) = l.map(_.asPath) + + var curSym = TempSymbol(None, "tmp") + val inst = Instantiate(c.sym.asPath, paramArgs) + var acc = blk => Assign(curSym, inst, blk) + for ps <- auxSyms do + val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true) + curSym = TempSymbol(None, "tmp") + acc = blk => acc(Assign(curSym, call, blk)) + val bod = acc(Return(Call(curSym.asPath, extraSyms.map(_.asPath.asArg))(true), false)) + + inline def toPlist(ls: List[VarSymbol]) = PlainParamList(ls.map(s => Param(FldFlags.empty, s, N))) + + val paramPlist = paramSyms.map(toPlist) + val auxPlist = auxSyms.map(toPlist) + val extraPlist = toPlist(extraSyms) + + val plist = paramPlist match + case None => extraPlist :: PlainParamList(Nil) :: auxPlist + case Some(value) => extraPlist :: value :: auxPlist + + val fakeCtorDefn = FunDefn( + None, bms, plist, bod + ) + + Lifted(lifted, extras.appended(fakeCtorDefn)) + case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted = val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns 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 b21a46eba..886ba142c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -166,13 +166,14 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: // * Note: `_pubFlds` is not used because in JS, fields are not declared val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) + val ctorAuxParams = auxParams.map(ps => ps.params.map(p => p.sym -> scope.allocateName(p.sym))) val privs = val scp = isym.asInstanceOf[InnerSymbol].privatesScope privFlds.map: fld => val nme = scp.allocateName(fld) doc" # #$nme;" .mkDocument(doc"") - val preCtorCode = ctorParams.foldLeft(body(preCtor)): + val preCtorCode = (ctorParams ++ ctorAuxParams.flatMap(ps => ps)).foldLeft(body(preCtor)): case (acc, (sym, nme)) => doc"$acc # this.${sym.name} = $nme;" val ctorCode = doc"$preCtorCode${body(ctor)}" @@ -180,12 +181,11 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: val ctorBod = if auxParams.isEmpty then doc"${braced(ctorCode)}" else - - val pss = auxParams.map(setupFunction(N, _, End())._1) + val pss = ctorAuxParams.map(_.map(_._2)) val newCtorCode = doc"$ctorCode; # return this;" val ctorBraced = doc"${ braced(newCtorCode) }" val funBod = pss.foldRight(ctorBraced): - case (psDoc, doc) => doc"($psDoc) => $doc" + case (psDoc, doc) => doc"(${psDoc.mkDocument(",")}) => $doc" doc"${ braced(doc" # return $funBod") }" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index fc1b040f3..696b2ee56 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -87,63 +87,7 @@ f(1, 2) //│ = 5 - -:todo -:sjs -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - Test(unused1) -f(1, 2).get() -//│ JS (unsanitized): -//│ let f7, Test1, g7, h1, tmp; -//│ h1 = function h(used3, used1) { -//│ return () => { -//│ return used3; -//│ }; -//│ }; -//│ g7 = function g(used1) { -//│ return (g$_arg) => { -//│ let used3, tmp1, h$this; -//│ used3 = 2; -//│ h$this = h1(used3, used1) ?? null; -//│ tmp1 = h$this(); -//│ return used1 + tmp1; -//│ }; -//│ }; -//│ Test1 = function Test(a1) { -//│ return (used11) => { -//│ return new Test.class(a1)(used11); -//│ } -//│ }; -//│ Test1.class = class Test { -//│ constructor(a) { -//│ return (used1) => { -//│ this.a = a;; -//│ return this; -//│ } -//│ } -//│ get() { -//│ return this.used1; -//│ } -//│ toString() { return "Test(" + this.a + ")"; } -//│ }; -//│ f7 = function f(used1, unused1) { -//│ let unused2, Test$this; -//│ unused2 = 2; -//│ Test$this = Test1(used1) ?? null; -//│ return Test$this(unused1); -//│ }; -//│ tmp = f7(1, 2); -//│ tmp.get() ?? null //│ (used11) => -//│ JS (unsanitized): -//│ null // preserve order :sjs @@ -152,24 +96,24 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f9, g9; -//│ g9 = function g(a1, a2, a3, a4, a5, a6) { +//│ let f7, g7; +//│ g7 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ let tmp1, tmp2, tmp3, tmp4; -//│ tmp1 = a1 + a2; -//│ tmp2 = tmp1 + a3; -//│ tmp3 = tmp2 + a4; -//│ tmp4 = tmp3 + a5; -//│ return tmp4 + a6; +//│ let tmp, tmp1, tmp2, tmp3; +//│ tmp = a1 + a2; +//│ tmp1 = tmp + a3; +//│ tmp2 = tmp1 + a4; +//│ tmp3 = tmp2 + a5; +//│ return tmp3 + a6; //│ }; //│ }; -//│ f9 = function f(a1, a2, a3, a4, a5, a6) { -//│ let tmp1, g$this; -//│ g$this = g9(a1, a2, a3, a4, a5, a6) ?? null; -//│ tmp1 = g$this(); -//│ return tmp1; +//│ f7 = function f(a1, a2, a3, a4, a5, a6) { +//│ let tmp, g$this; +//│ g$this = g7(a1, a2, a3, a4, a5, a6) ?? null; +//│ tmp = g$this(); +//│ return tmp; //│ }; -//│ f9(1, 2, 3, 4, 5, 6) +//│ f7(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' @@ -188,14 +132,14 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f11, Tuple1, g11, h3, ret, f12, f21, x, y, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, f$capture1; -//│ g11 = function g(f$capture2) { +//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture1; +//│ g9 = function g(f$capture2) { //│ return () => { //│ f$capture2.a0$ = 1; //│ return null; //│ }; //│ }; -//│ h3 = function h(f$capture2) { +//│ h1 = function h(f$capture2) { //│ return () => { //│ return f$capture2.a0$; //│ }; @@ -209,12 +153,12 @@ x + y //│ } //│ toString() { return "f$capture(" + this.a0$ + ")"; } //│ }; -//│ f11 = function f() { +//│ f9 = function f() { //│ let capture, g$this, h$this; //│ capture = new f$capture1(); //│ capture.a0$ = 0; -//│ g$this = g11(capture) ?? null; -//│ h$this = h3(capture) ?? null; +//│ g$this = g9(capture) ?? null; +//│ h$this = h1(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; //│ Tuple1 = function Tuple(a1, b1) { @@ -227,17 +171,17 @@ x + y //│ } //│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } //│ }; -//│ tmp1 = f11(); -//│ ret = tmp1; -//│ f12 = ret.a; +//│ tmp = f9(); +//│ ret = tmp; +//│ f11 = ret.a; //│ f21 = ret.b; -//│ tmp2 = f21() ?? null; -//│ tmp3 = tmp2.toString() ?? null; -//│ x = tmp3; -//│ tmp4 = f12() ?? null; -//│ tmp5 = f21() ?? null; -//│ tmp6 = tmp5.toString() ?? null; -//│ y = tmp6; +//│ tmp1 = f21() ?? null; +//│ tmp2 = tmp1.toString() ?? null; +//│ x = tmp2; +//│ tmp3 = f11() ?? null; +//│ tmp4 = f21() ?? null; +//│ tmp5 = tmp4.toString() ?? null; +//│ y = tmp5; //│ x + y //│ = '01' //│ f1 = [Function (anonymous)] @@ -295,14 +239,14 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f16, g15, h7, f$capture5; -//│ g15 = function g(immutable, f$capture6) { +//│ let f14, g13, h5, f$capture5; +//│ g13 = function g(immutable, f$capture6) { //│ return () => { //│ f$capture6.mutated0$ = 2; //│ return immutable + f$capture6.mutated0$; //│ }; //│ }; -//│ h7 = function h(immutable, f$capture6) { +//│ h5 = function h(immutable, f$capture6) { //│ return () => { //│ return f$capture6.mutated0$; //│ }; @@ -316,18 +260,18 @@ f(1, 2, 1000) //│ } //│ toString() { return "f$capture(" + this.mutated0$ + ")"; } //│ }; -//│ f16 = function f(unused, immutable, mutated) { -//│ let a1, tmp22, tmp23, tmp24, capture, g$this, h$this; +//│ f14 = function f(unused, immutable, mutated) { +//│ let a1, tmp21, tmp22, tmp23, capture, g$this, h$this; //│ capture = new f$capture5(mutated); -//│ g$this = g15(immutable, capture) ?? null; -//│ tmp22 = g$this(); -//│ a1 = tmp22; -//│ h$this = h7(immutable, capture) ?? null; -//│ tmp23 = h$this(); -//│ tmp24 = a1 + tmp23; -//│ return tmp24 + unused; -//│ }; -//│ f16(1, 2, 1000) +//│ g$this = g13(immutable, capture) ?? null; +//│ tmp21 = g$this(); +//│ a1 = tmp21; +//│ h$this = h5(immutable, capture) ?? null; +//│ tmp22 = h$this(); +//│ tmp23 = a1 + tmp22; +//│ return tmp23 + unused; +//│ }; +//│ f14(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -359,39 +303,39 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let f23, g23, h9; -//│ g23 = function g(arg) { +//│ let f20, g19, h7; +//│ g19 = function g(arg) { //│ return (n) => { -//│ let scrut, tmp22, h$this; +//│ let scrut, tmp21, h$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { -//│ tmp22 = n - 1; -//│ h$this = h9(arg) ?? null; -//│ return h$this(tmp22); +//│ tmp21 = n - 1; +//│ h$this = h7(arg) ?? null; +//│ return h$this(tmp21); //│ } //│ }; //│ }; -//│ h9 = function h(arg) { +//│ h7 = function h(arg) { //│ return (n) => { -//│ let scrut, tmp22, g$this; +//│ let scrut, tmp21, g$this; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { -//│ tmp22 = n - 1; -//│ g$this = g23(arg) ?? null; -//│ return g$this(tmp22); +//│ tmp21 = n - 1; +//│ g$this = g19(arg) ?? null; +//│ return g$this(tmp21); //│ } //│ }; //│ }; -//│ f23 = function f(arg) { +//│ f20 = function f(arg) { //│ let h$this; -//│ h$this = h9(arg) ?? null; +//│ h$this = h7(arg) ?? null; //│ return h$this(5); //│ }; -//│ f23(2) +//│ f20(2) //│ = 2 :expect 1 @@ -405,6 +349,88 @@ f() /// CLASSES /// +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + Test(unused1) +f(1, 2).get() +//│ = 1 + +:fixme +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + new Test(unused1) +f(1, 2).get() +//│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor + +:expect 2 +:sjs +fun f(x) = + class A() with + fun f() = set x = 2 + A().f() + x +f(1) +//│ JS (unsanitized): +//│ let f31, A1, A$ctor1, f$capture9; +//│ A$ctor1 = function A$ctor(f$capture10) { +//│ return () => { +//│ let tmp23; +//│ tmp23 = new A1(); +//│ return tmp23(f$capture10); +//│ }; +//│ }; +//│ A1 = function A() { +//│ return (f$capture11) => { +//│ return new A.class()(f$capture11); +//│ } +//│ }; +//│ A1.class = class A { +//│ constructor() { +//│ return (f$capture10) => { +//│ this.f$capture = f$capture10;; +//│ return this; +//│ } +//│ } +//│ f() { +//│ this.f$capture.x0$ = 2; +//│ return null; +//│ } +//│ toString() { return "A(" + + ")"; } +//│ }; +//│ f$capture9 = function f$capture(x0$1) { +//│ return new f$capture.class(x0$1); +//│ }; +//│ f$capture9.class = class f$capture { +//│ constructor(x0$) { +//│ this.x0$ = x0$; +//│ } +//│ toString() { return "f$capture(" + this.x0$ + ")"; } +//│ }; +//│ f31 = function f(x1) { +//│ let tmp23, tmp24, capture, A$this; +//│ capture = new f$capture9(x1); +//│ A$this = A$ctor1(capture) ?? null; +//│ tmp23 = A$this(); +//│ tmp24 = tmp23.f() ?? null; +//│ return capture.x0$; +//│ }; +//│ f31(1) +//│ = 2 + :sjs class A(a) with fun f(b) = @@ -414,41 +440,41 @@ class A(a) with g A(1).f(1)(3) //│ JS (unsanitized): -//│ let A1, g27, tmp22, tmp23, f$capture9; -//│ g27 = function g(A$instance, f$capture10) { +//│ let A3, g29, tmp23, tmp24, f$capture11; +//│ g29 = function g(A$instance, f$capture12) { //│ return (c1) => { -//│ let tmp24; -//│ f$capture10.b0$ = 2; -//│ tmp24 = A$instance.a + f$capture10.b0$; -//│ return tmp24 + c1; +//│ let tmp25; +//│ f$capture12.b0$ = 2; +//│ tmp25 = A$instance.a + f$capture12.b0$; +//│ return tmp25 + c1; //│ }; //│ }; -//│ f$capture9 = function f$capture(b0$1) { +//│ f$capture11 = function f$capture(b0$1) { //│ return new f$capture.class(b0$1); //│ }; -//│ f$capture9.class = class f$capture { +//│ f$capture11.class = class f$capture { //│ constructor(b0$) { //│ this.b0$ = b0$; //│ } //│ toString() { return "f$capture(" + this.b0$ + ")"; } //│ }; -//│ A1 = function A(a2) { +//│ A3 = function A(a2) { //│ return new A.class(a2); //│ }; -//│ A1.class = class A { +//│ A3.class = class A { //│ constructor(a1) { //│ this.a = a1; //│ } //│ f(b1) { //│ let capture; -//│ capture = new f$capture9(b1); -//│ return g27(this, capture) ?? null; +//│ capture = new f$capture11(b1); +//│ return g29(this, capture) ?? null; //│ } //│ toString() { return "A(" + this.a + ")"; } //│ }; -//│ tmp22 = A1(1); -//│ tmp23 = tmp22.f(1) ?? null; -//│ tmp23(3) ?? null +//│ tmp23 = A3(1); +//│ tmp24 = tmp23.f(1) ?? null; +//│ tmp24(3) ?? null //│ = 6 // Here, g does not need the capture. This should be optimized @@ -463,36 +489,36 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f30, f31, g29, f$capture11; -//│ g29 = function g(f$capture12) { +//│ let f34, f35, g31, f$capture13; +//│ g31 = function g(f$capture14) { //│ return () => { //│ return 1; //│ }; //│ }; -//│ f31 = function f(f$capture12) { +//│ f35 = function f(f$capture14) { //│ return () => { -//│ f$capture12.x0$ = 1; +//│ f$capture14.x0$ = 1; //│ return null; //│ }; //│ }; -//│ f$capture11 = function f$capture(x0$1) { +//│ f$capture13 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); //│ }; -//│ f$capture11.class = class f$capture { +//│ f$capture13.class = class f$capture { //│ constructor(x0$) { //│ this.x0$ = x0$; //│ } //│ toString() { return "f$capture(" + this.x0$ + ")"; } //│ }; -//│ f30 = function f() { -//│ let tmp24, capture, f$this; -//│ capture = new f$capture11(); +//│ f34 = function f() { +//│ let tmp25, capture, f$this; +//│ capture = new f$capture13(); //│ capture.x0$ = 1; -//│ f$this = f31(capture) ?? null; -//│ tmp24 = f$this(); +//│ f$this = f35(capture) ?? null; +//│ tmp25 = f$this(); //│ return capture.x0$; //│ }; -//│ f30() +//│ f34() //│ = 1 // don't break private variables From 175ced84461c9e30ab03140801eb6747a4e31820 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 30 Jan 2025 01:50:44 +0800 Subject: [PATCH 027/127] lift lambdas and adapt handler code to work with class lifting --- .../scala/hkmc2/codegen/HandlerLowering.scala | 15 ++++---- .../src/main/scala/hkmc2/codegen/Lifter.scala | 37 ++++++++++++++++--- .../scala/hkmc2/codegen/js/JSBuilder.scala | 2 +- .../src/test/mlscript/codegen/Lifter.mls | 37 +++++++++++++++++++ .../src/test/mlscript/handlers/Effects.mls | 13 ++++--- 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 22b831628..a2f516228 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -53,9 +53,8 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): HandlerCtx(true, false, ctorThis, state => val tmp = freshTmp() blockBuilder - .assignFieldN(state.res.tail, nextIdent, Instantiate( - state.cls.selN(Tree.Ident("class")), - Value.Lit(Tree.IntLit(state.uid)) :: Nil)) + .assignFieldN(state.res.tail, nextIdent, Call( + state.cls, Value.Lit(Tree.IntLit(state.uid)).asArg :: Nil)(true)) .assignFieldN(state.res, tailIdent, state.res.tail.next) .ret(state.res)) private val functionHandlerCtx = funcLikeHandlerCtx(N) @@ -362,14 +361,14 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): b match case Return(res, implct) => // In case res is effectful, it will be handled in translateBlock - Assign(tmp, res, Return(Instantiate(retClsPath, tmp.asPath :: Nil), implct)) + Assign(tmp, res, Return(Call(retClsPath, tmp.asPath.asArg :: Nil)(true), implct)) case HandleBlockReturn(res) => Return(res, false) case _ => super.applyBlock(b) transform.applyBlock(b) val handlerBody = translateBlock(prepareBody(h.body), HandlerCtx(false, false, N, state => blockBuilder - .assignFieldN(state.res.tail, nextIdent, Instantiate(state.cls, Value.Lit(Tree.IntLit(state.uid)) :: Nil)) + .assignFieldN(state.res.tail, nextIdent, Call(state.cls, Value.Lit(Tree.IntLit(state.uid)).asArg :: Nil)(true)) .ret(SimpleCall(handleBlockImplPath, state.res :: h.lhs.asPath :: Nil)))) val handlers = h.handlers.map: handler => @@ -379,7 +378,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val tmp = freshTmp() FunDefn( S(h.cls), - handler.sym, handler.params, Return(SimpleCall(mkEffectPath, h.lhs.asPath :: lam :: Nil), false)) + handler.sym, handler.params, Return(SimpleCall(mkEffectPath, h.cls.asPath :: lam :: Nil), false)) // TODO: it seems that our current syntax didn't know how to call super, calling it with empty param list now val clsDefn = ClsLikeDefn( @@ -387,13 +386,13 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): h.cls, BlockMemberSymbol(h.cls.id.name, Nil), syntax.Cls, - N, Nil, + S(PlainParamList(Nil)), Nil, S(h.par), handlers, Nil, Nil, Assign(freshTmp(), SimpleCall(Value.Ref(State.builtinOpsMap("super")), Nil), End()), End()) val body = blockBuilder .define(clsDefn) - .assign(h.lhs, Instantiate(Value.Ref(clsDefn.sym), Nil)) + .assign(h.lhs, Call(clsDefn.sym.asPath, Nil)(true)) .rest(handlerBody) val defn = FunDefn( diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 913bedaec..8f1a2cf7a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -539,7 +539,7 @@ class Lifter(using State): liftDefnsInFn(newDef, newCtx) case c: ClsLikeDefn => val newDef = c.copy( - owner = base.owner, auxParams = c.auxParams.appended(PlainParamList(extraParams)) + owner = N, auxParams = c.auxParams.appended(PlainParamList(extraParams)) ) val Lifted(lifted, extras) = liftDefnsInCls(newDef, newCtx) @@ -606,8 +606,8 @@ class Lifter(using State): case Lifted(liftedDefn, extraDefns) => extraDefns val extras = (ctorDefnsLifted ++ fExtra).map: - case f: FunDefn => f.copy(owner = c.owner) - case c: ClsLikeDefn => c.copy(owner = c.owner) + case f: FunDefn => f.copy(owner = N) + case c: ClsLikeDefn => c.copy(owner = N) case d => d val newDef = c.copy( @@ -656,10 +656,35 @@ class Lifter(using State): end liftDefnsInFn + def desugarLambdas(b: Block) = + def rewriteOneBlk(b: Block) = + var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil + val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): + override def applyValue(v: Value): Value = v match + case lam: Value.Lam => + val sym = BlockMemberSymbol("lambda", Nil) + lambdasList ::= (sym -> super.applyLam(lam)) + Value.Ref(sym) + case _ => super.applyValue(v) + val blk = lambdaRewriter.applyBlock(b) + (blk, lambdasList) + + val transformer = new BlockTransformer(SymbolSubst()): + override def applyBlock(b: Block): Block = + val (newBlk, lambdasList) = rewriteOneBlk(b) + val lambdaDefns = lambdasList.map: + case (sym, Value.Lam(params, body)) => + FunDefn(None, sym, params :: Nil, body) + val ret = lambdaDefns.foldLeft(newBlk): + case (acc, defn) => Define(defn, acc) + super.applyBlock(ret) + transformer.applyBlock(b) + // top-level def transform(b: Block) = - val ctx = LifterCtx.withLocals(findUsedLocals(b)) - val ctxx = ctx.addBmsReqdInfo(createLiftInfo(b, ctx)) + val blk = desugarLambdas(b) + val ctx = LifterCtx.withLocals(findUsedLocals(blk)) + val ctxx = ctx.addBmsReqdInfo(createLiftInfo(blk, ctx)) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match @@ -670,4 +695,4 @@ class Lifter(using State): case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) - walker.applyBlock(b) \ No newline at end of file + walker.applyBlock(blk) \ No newline at end of file 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 886ba142c..159603299 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -185,7 +185,7 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: val newCtorCode = doc"$ctorCode; # return this;" val ctorBraced = doc"${ braced(newCtorCode) }" val funBod = pss.foldRight(ctorBraced): - case (psDoc, doc) => doc"(${psDoc.mkDocument(",")}) => $doc" + case (psDoc, doc) => doc"(${psDoc.mkDocument(", ")}) => $doc" doc"${ braced(doc" # return $funBod") }" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 696b2ee56..d7f75f746 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -541,3 +541,40 @@ class A(a) with g() A(2).f() //│ ═══[RUNTIME ERROR] Expected: 2, got: null + +:lot +:sjs +class B() +//│ JS (unsanitized): +//│ let B1; +//│ B1 = function B() { +//│ return new B.class(); +//│ }; +//│ B1.class = class B { +//│ constructor() {} +//│ toString() { return "B(" + + ")"; } +//│ }; +//│ null +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Define: +//│ defn = ClsLikeDefn: +//│ owner = N +//│ isym = class:B +//│ sym = member:B +//│ k = Cls +//│ paramsOpt = S of ParamList: +//│ flags = ParamListFlags of false +//│ params = Nil +//│ restParam = N +//│ auxParams = Nil +//│ parentPath = N +//│ methods = Nil +//│ privateFields = Nil +//│ publicFields = Nil +//│ preCtor = End of "" +//│ ctor = End of "" +//│ rest = Return: \ +//│ res = Lit of UnitLit of true +//│ implct = true diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 89a9cd222..00e4348c1 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -159,19 +159,22 @@ if true do //│ let tmp11, handleBlock$23; //│ handleBlock$23 = function handleBlock$() { //│ let h, scrut, f, tmp12, res, Cont$, Effect$h$; -//│ Effect$h$ = class Effect$h$ extends Effect1 { +//│ Effect$h$ = function Effect$h$() { +//│ return new Effect$h$.class(); +//│ }; +//│ Effect$h$.class = class Effect$h$ extends Effect1 { //│ constructor() { //│ let tmp13; //│ tmp13 = super(); //│ } //│ perform(arg) { -//│ return globalThis.Predef.__mkEffect(h, (k) => { +//│ return globalThis.Predef.__mkEffect(this, (k) => { //│ return arg; //│ }); //│ } -//│ toString() { return "Effect$h$"; } +//│ toString() { return "Effect$h$(" + + ")"; } //│ }; -//│ h = new Effect$h$(); +//│ h = Effect$h$(); //│ Cont$ = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; @@ -205,7 +208,7 @@ if true do //│ if (scrut === true) { //│ res = f(); //│ if (res instanceof globalThis.Predef.__EffectSig.class) { -//│ res.tail.next = new Cont$(0); +//│ res.tail.next = Cont$(0); //│ return globalThis.Predef.__handleBlockImpl(res, h); //│ } //│ tmp12 = res; From 71e48dd2606287e0ba80fca753f8a62d463d164b Mon Sep 17 00:00:00 2001 From: Anson Yeung Date: Thu, 30 Jan 2025 02:42:43 +0800 Subject: [PATCH 028/127] Fix bug in capture instantiation --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 7 +- .../src/test/mlscript/codegen/Lifter.mls | 66 ++++++++++++++++++- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 8f1a2cf7a..c75a3687b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -647,10 +647,11 @@ class Lifter(using State): else // move the function's parameters to the capture val paramsSet = f.params.flatMap(_.paramSyms) - val paramsList = varsList.filter(paramsSet.contains(_)) + val paramsList = varsList.map: s => + if paramsSet.contains(s) then s.asPath else Value.Lit(Tree.UnitLit(true)) // moved when the capture is instantiated val bod = blockBuilder - .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList.map(_.asPath))) + .assign(captureSym, Instantiate(captureCls.sym.asPath, paramsList)) .rest(transformed) Lifted(FunDefn(f.owner, f.sym, f.params, bod), captureCls :: newDefns) @@ -695,4 +696,4 @@ class Lifter(using State): case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) - walker.applyBlock(blk) \ No newline at end of file + walker.applyBlock(blk) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index d7f75f746..3c4d52d12 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -155,7 +155,7 @@ x + y //│ }; //│ f9 = function f() { //│ let capture, g$this, h$this; -//│ capture = new f$capture1(); +//│ capture = new f$capture1(null); //│ capture.a0$ = 0; //│ g$this = g9(capture) ?? null; //│ h$this = h1(capture) ?? null; @@ -512,7 +512,7 @@ f() //│ }; //│ f34 = function f() { //│ let tmp25, capture, f$this; -//│ capture = new f$capture13(); +//│ capture = new f$capture13(null); //│ capture.x0$ = 1; //│ f$this = f35(capture) ?? null; //│ tmp25 = f$this(); @@ -578,3 +578,65 @@ class B() //│ rest = Return: \ //│ res = Lit of UnitLit of true //│ implct = true + +:sjs +fun g() = + let y = 0 + fun f(x) = + let k = 4 + set y = 2 + // set x = 3 + // just a dummy function to force class generation + fun h() = + set k = 5 + set x = 4 + x + y + x + f +g()(1) +//│ JS (unsanitized): +//│ let g35, f37, h13, tmp27, g$capture1, f$capture15; +//│ h13 = function h(f$capture16, g$capture2) { +//│ return () => { +//│ f$capture16.k0$ = 5; +//│ f$capture16.x1$ = 4; +//│ return f$capture16.x1$ + g$capture2.y0$; +//│ }; +//│ }; +//│ f$capture15 = function f$capture(k0$1, x1$1) { +//│ return new f$capture.class(k0$1, x1$1); +//│ }; +//│ f$capture15.class = class f$capture { +//│ constructor(k0$, x1$) { +//│ this.k0$ = k0$; +//│ this.x1$ = x1$; +//│ } +//│ toString() { return "f$capture(" + this.k0$ + ", " + this.x1$ + ")"; } +//│ }; +//│ f37 = function f(g$capture2) { +//│ return (x1) => { +//│ let capture; +//│ capture = new f$capture15(null, x1); +//│ capture.k0$ = 4; +//│ g$capture2.y0$ = 2; +//│ return capture.x1$; +//│ }; +//│ }; +//│ g$capture1 = function g$capture(y0$1) { +//│ return new g$capture.class(y0$1); +//│ }; +//│ g$capture1.class = class g$capture { +//│ constructor(y0$) { +//│ this.y0$ = y0$; +//│ } +//│ toString() { return "g$capture(" + this.y0$ + ")"; } +//│ }; +//│ g35 = function g() { +//│ let capture; +//│ capture = new g$capture1(null); +//│ capture.y0$ = 0; +//│ return f37(capture) ?? null; +//│ }; +//│ tmp27 = g35(); +//│ tmp27(1) ?? null +//│ = 1 From ce089fed1b4b81ec48ff6560d2080820417aa579 Mon Sep 17 00:00:00 2001 From: Anson Yeung Date: Thu, 30 Jan 2025 03:02:18 +0800 Subject: [PATCH 029/127] Remove extra semicolon --- hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala | 4 ++-- hkmc2/shared/src/test/mlscript/codegen/Lifter.mls | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 159603299..9769ac5fd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -182,12 +182,12 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: doc"${braced(ctorCode)}" else val pss = ctorAuxParams.map(_.map(_._2)) - val newCtorCode = doc"$ctorCode; # return this;" + val newCtorCode = doc"$ctorCode # return this;" val ctorBraced = doc"${ braced(newCtorCode) }" val funBod = pss.foldRight(ctorBraced): case (psDoc, doc) => doc"(${psDoc.mkDocument(", ")}) => $doc" - doc"${ braced(doc" # return $funBod") }" + doc"${ braced(doc" # return $funBod") }" val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ privs diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 3c4d52d12..68e821cc0 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -401,7 +401,7 @@ f(1) //│ A1.class = class A { //│ constructor() { //│ return (f$capture10) => { -//│ this.f$capture = f$capture10;; +//│ this.f$capture = f$capture10; //│ return this; //│ } //│ } From 7ee2c45e363b80aa21b266071207bbff5a94c323 Mon Sep 17 00:00:00 2001 From: Anson Yeung Date: Thu, 30 Jan 2025 04:07:45 +0800 Subject: [PATCH 030/127] Allow paths in class name --- .../scala/hkmc2/semantics/Elaborator.scala | 6 ++-- .../src/test/mlscript/parser/Handler.mls | 31 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index add5356c0..a41293e56 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -243,7 +243,7 @@ extends Importer: case _ => ??? // TODO error case Hndl(id, cls, blk, S(bod)) => term(Block(Hndl(id, cls, blk, N) :: bod :: Nil)) - case Hndl(id: Ident, cls: Ident, Block(sts), N) => + case Hndl(id: Ident, cls, Block(sts), N) => raise(ErrorReport( msg"Expected a body for handle bindings in expression position" -> tree.toLoc :: Nil)) @@ -711,13 +711,13 @@ extends Importer: case (tree @ LetLike(`let`, lhs, S(rhs), N)) :: sts => raise(ErrorReport(msg"Unsupported let binding shape" -> tree.toLoc :: Nil)) go(sts, funs, Nil, Term.Error :: acc) - case (hd @ Hndl(id: Ident, cls: Ident, Block(sts_), N)) :: sts => + case (hd @ Hndl(id: Ident, cls, Block(sts_), N)) :: sts => reportUnusedAnnotations val res: Term.Blk = ctx.nest(N).givenIn: val sym = fieldOrVarSym(HandlerBind, id) log(s"Processing `handle` statement $id (${sym}) ${ctx.outer}") - val derivedClsSym = ClassSymbol(Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), Tree.Ident(s"${cls.name}$$${id.name}$$")) + val derivedClsSym = ClassSymbol(Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), Tree.Ident(s"Handler$$${id.name}$$")) derivedClsSym.defn = S(ClassDef( N, syntax.Cls, derivedClsSym, BlockMemberSymbol(derivedClsSym.name, Nil), diff --git a/hkmc2/shared/src/test/mlscript/parser/Handler.mls b/hkmc2/shared/src/test/mlscript/parser/Handler.mls index 803385270..2137d5fc9 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Handler.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Handler.mls @@ -52,7 +52,7 @@ handle h = Eff with fun f()(r) = r(0) in foo(h) -//│ Elab: { { handle h = Ref(member:Eff) List(HandlerTermDefinition(r,TermDefinition(Some(class:Eff$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List()))); { member:foo#666(h#666) } } } +//│ Elab: { { handle h = Ref(member:Eff) List(HandlerTermDefinition(r,TermDefinition(Some(class:Handler$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List()))); { member:foo#666(h#666) } } } :e ( @@ -68,12 +68,15 @@ in //│ ║ l.61: fun g(a)()(r) = r(1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ +module Mod with + abstract class Eff + :el -handle h = Eff with +handle h = Mod.Eff with fun f()(r) = r(0) fun g(a)()()(r) = r(1) foo(h) -//│ Elab: { handle h = Ref(member:Eff) List(HandlerTermDefinition(r,TermDefinition(Some(class:Eff$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List())), HandlerTermDefinition(r,TermDefinition(Some(class:Eff$h$),Fun,member:g,List(ParamList(‹›,List(Param(‹›,a,None)),None), ParamList(‹›,List(),None), ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹result of member:g›,‹›,List()))); { member:foo#666(h#666) } } +//│ Elab: { handle h = Sel(Ref(member:Mod),Ident(Eff)) List(HandlerTermDefinition(r,TermDefinition(Some(class:Handler$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List())), HandlerTermDefinition(r,TermDefinition(Some(class:Handler$h$),Fun,member:g,List(ParamList(‹›,List(Param(‹›,a,None)),None), ParamList(‹›,List(),None), ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹result of member:g›,‹›,List()))); { member:foo#666(h#666) } } :e handle h = Eff with @@ -84,10 +87,10 @@ handle h = Eff with fun x() = x foo(h) //│ ╔══[ERROR] Only function definitions are allowed in handler blocks -//│ ║ l.82: val x = 24 +//│ ║ l.85: val x = 24 //│ ╙── ^^ //│ ╔══[ERROR] Only function definitions are allowed in handler blocks -//│ ║ l.84: fun x() = x +//│ ║ l.87: fun x() = x //│ ╙── ^ :e @@ -102,19 +105,19 @@ handle h = Eff with foo(h) foo(h) //│ ╔══[ERROR] Handler function is missing resumption parameter -//│ ║ l.98: fun f = r(0) -//│ ╙── ^^^^ +//│ ║ l.101: fun f = r(0) +//│ ╙── ^^^^ //│ ╔══[ERROR] Handler function is missing resumption parameter -//│ ║ l.99: fun g() = r(0) -//│ ╙── ^^^^ +//│ ║ l.102: fun g() = r(0) +//│ ╙── ^^^^ //│ ╔══[ERROR] Handler function is missing resumption parameter -//│ ║ l.100: fun h()() = r(1) +//│ ║ l.103: fun h()() = r(1) //│ ╙── ^^^^ //│ ╔══[ERROR] Handler function is missing resumption parameter -//│ ║ l.101: fun h2()(a, b) = r(1) +//│ ║ l.104: fun h2()(a, b) = r(1) //│ ╙── ^^^^ //│ ╔══[ERROR] Name not found: h -//│ ║ l.102: foo(h) +//│ ║ l.105: foo(h) //│ ╙── ^ :w @@ -125,6 +128,6 @@ handle h = Eff with 12345 foo(h) //│ ╔══[WARNING] Terms in handler block do nothing -//│ ║ l.125: 12345 +//│ ║ l.128: 12345 //│ ╙── ^^^^^ -//│ Elab: { handle h = Ref(member:Eff) List(HandlerTermDefinition(r,TermDefinition(Some(class:Eff$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List())), HandlerTermDefinition(r,TermDefinition(Some(class:Eff$h$),Fun,member:g,List(ParamList(‹›,List(Param(‹›,a,None)),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹result of member:g›,‹›,List()))); { member:foo#666(h#666) } } +//│ Elab: { handle h = Ref(member:Eff) List(HandlerTermDefinition(r,TermDefinition(Some(class:Handler$h$),Fun,member:f,List(ParamList(‹›,List(),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹result of member:f›,‹›,List())), HandlerTermDefinition(r,TermDefinition(Some(class:Handler$h$),Fun,member:g,List(ParamList(‹›,List(Param(‹›,a,None)),None)),None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹result of member:g›,‹›,List()))); { member:foo#666(h#666) } } From 8aac8cdb7accde41c503569eebe53dcb952484bf Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 30 Jan 2025 23:32:39 +0800 Subject: [PATCH 031/127] optimize function calls, fix tests --- .../scala/hkmc2/codegen/HandlerLowering.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 86 +++- .../src/test/mlscript/codegen/Lifter.mls | 485 ++++++++++-------- .../src/test/mlscript/handlers/Effects.mls | 12 +- .../mlscript/handlers/EffectsInClasses.mls | 8 +- .../mlscript/handlers/RecursiveHandlers.mls | 196 +++---- .../test/mlscript/handlers/StackSafety.mls | 36 +- 7 files changed, 463 insertions(+), 362 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index a2f516228..6dc95cda9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -361,7 +361,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): b match case Return(res, implct) => // In case res is effectful, it will be handled in translateBlock - Assign(tmp, res, Return(Call(retClsPath, tmp.asPath.asArg :: Nil)(true), implct)) + Assign(tmp, res, Return(Instantiate(retClsPath, tmp.asPath :: Nil), implct)) case HandleBlockReturn(res) => Return(res, false) case _ => super.applyBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index c75a3687b..2f8c7c3f5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -314,11 +314,12 @@ class Lifter(using State): val reqdCaptures: List[BlockMemberSymbol], val reqdVars: List[Local], val reqdInnerSyms: List[InnerSymbol], - val fakeCtorBms: Option[BlockMemberSymbol] // only for classes + val fakeCtorBms: Option[BlockMemberSymbol], // only for classes + val singleCallBms: BlockMemberSymbol // optimization ) - case class Lifted( - val liftedDefn: Defn, + case class Lifted[+T <: Defn]( + val liftedDefn: T, val extraDefns: List[Defn], ) @@ -354,7 +355,9 @@ class Lifter(using State): case c: ClsLikeDefn => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) case _ => N - val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms) + val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) + + val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms, singleCallBms) if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then Map.empty else d match @@ -398,8 +401,16 @@ class Lifter(using State): val walker = new BlockTransformerNoRec(SymbolSubst()): // only scan within the block. don't traverse - // if possible, directly create the call and replace the result with it + override def applyResult(r: Result): Result = r match + // if possible, directly rewrite the call using the efficient version + case c @ Call(Value.Ref(l: BlockMemberSymbol), args) => ctx.bmsReqdInfo.get(l) match + case Some(info) => + val extraArgs = getCallArgs(l, ctx) + val newArgs = args.map(applyArg(_)) + Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun) + case None => super.applyResult(r) + // if possible, directly create the bms and replace the result with it case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) case _ => super.applyResult(r) @@ -475,24 +486,24 @@ class Lifter(using State): b |> transformer1.applyBlock |> transformer2.applyBlock - - - def createCall(sym: BlockMemberSymbol, ctx: LifterCtx) : Call = + def getCallArgs(sym: BlockMemberSymbol, ctx: LifterCtx) = val info = ctx.getBmsReqdInfo(sym).get val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) - + iSymArgs ++ localsArgs ++ capturesArgs + + def createCall(sym: BlockMemberSymbol, ctx: LifterCtx): Call = + val info = ctx.getBmsReqdInfo(sym).get val callSym = info.fakeCtorBms match case Some(v) => v case None => sym - - Call(callSym.asPath, iSymArgs ++ localsArgs ++ capturesArgs)(false) + Call(callSym.asPath, getCallArgs(sym, ctx))(false) // deals with creating parameter lists - def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted = ctx.getBmsReqdInfo(d.sym) match + def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match case N => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms, singleCallBms)) => val createSym = d match case d: ClsLikeDefn => ((nme: String) => TermSymbol(syntax.ParamBind, S(d.isym), Tree.Ident(nme))) case _ => ((nme: String) => VarSymbol(Tree.Ident(nme))) @@ -533,10 +544,33 @@ class Lifter(using State): d match case f: FunDefn => + // 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, PlainParamList(extraParams) :: f.params, f.body ) - liftDefnsInFn(newDef, newCtx) + 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)) // TODO: restParams not considered + + val mainDefn = FunDefn(f.owner, f.sym, PlainParamList(extraParamsCpy) :: headPlistCopy :: Nil, bdy) + val auxDefn = FunDefn(N, singleCallBms, flatPlist, lifted.body) + + + Lifted(mainDefn, auxDefn :: extras) case c: ClsLikeDefn => val newDef = c.copy( owner = N, auxParams = c.auxParams.appended(PlainParamList(extraParams)) @@ -580,11 +614,26 @@ class Lifter(using State): 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 = paramPlist match + case None => extraPlist + case Some(value) => ParamList(value.flags, extraPlist.params ++ value.params, value.restParam) + + val auxCtorDefn_ = FunDefn(None, singleCallBms, headParams :: auxPlist, bod) + val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) - Lifted(lifted, extras.appended(fakeCtorDefn)) + Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) case _ => Lifted(d, Nil) - def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted = + def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns val (ctor, ctorDefns) = c.ctor.floatOutDefns @@ -600,8 +649,7 @@ class Lifter(using State): val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) val methods = fLifted.collect: - // the type check FunDefn here does nothing, this is just to satisfy the tye system - case Lifted(liftedDefn: FunDefn, extraDefns) => liftedDefn + case Lifted(liftedDefn, extraDefns) => liftedDefn val fExtra = fLifted.flatMap: case Lifted(liftedDefn, extraDefns) => extraDefns @@ -618,7 +666,7 @@ class Lifter(using State): Lifted(newDef, extras) - def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted = + def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) val (blk, nested) = f.body.floatOutDefns diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 68e821cc0..56b1a7da2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -12,25 +12,76 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f1, g1; +//│ let f1, g1, g$1; +//│ g$1 = function g$(used1, used2, g$_arg) { +//│ let used3; +//│ used3 = 2; +//│ return used1 + used2; +//│ }; //│ g1 = function g(used1, used2) { //│ return (g$_arg) => { -//│ let used3; -//│ used3 = 2; -//│ return used1 + used2; +//│ return g$1(used1, used2, g$_arg); //│ }; //│ }; //│ f1 = function f(used1, unused1) { -//│ let used2, unused2, tmp, g$this; +//│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ g$this = g1(used1, used2) ?? null; -//│ tmp = g$this(used2); +//│ tmp = g$1(used1, used2, used2); //│ return tmp + unused2; //│ }; //│ f1(1, 2) //│ = 5 +:expect 9 +:sjs +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg)(g_arg2) = + let used3 = 2 + used1 + used2 + g_arg + g_arg2 + let unused2 = 2 + g(used2)(used2) + unused2 +f(1, 2) +//│ JS (unsanitized): +//│ let f3, g3, g$3; +//│ g$3 = function g$(used1, used2, g$_arg) { +//│ return (g$_arg2) => { +//│ let used3, tmp, tmp1; +//│ used3 = 2; +//│ tmp = used1 + used2; +//│ tmp1 = tmp + g$_arg; +//│ return tmp1 + g$_arg2; +//│ }; +//│ }; +//│ g3 = function g(used1, used2) { +//│ return (g$_arg) => { +//│ return g$3(used1, used2, g$_arg); +//│ }; +//│ }; +//│ f3 = function f(used1, unused1) { +//│ let used2, unused2, tmp, tmp1; +//│ used2 = unused1; +//│ unused2 = 2; +//│ tmp = g$3(used1, used2, used2); +//│ tmp1 = tmp(used2) ?? null; +//│ return tmp1 + unused2; +//│ }; +//│ f3(1, 2) +//│ = 9 + +:expect 9 +fun f(used1, unused1) = + let used2 = unused1 + fun g(g_arg)(g_arg2) = + let used3 = 2 + used1 + used2 + g_arg + g_arg2 + let unused2 = 2 + let foo = g + foo(used2)(used2) + unused2 +f(1, 2) +//│ = 9 + :expect 5 :sjs fun f(used1, unused1) = @@ -43,23 +94,26 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f3, g3; -//│ g3 = function g(used1, used2) { +//│ let f7, g7, g$7; +//│ g$7 = function g$(used1, used2, g$_arg) { +//│ let used3; +//│ used3 = 2; +//│ return used1 + used2; +//│ }; +//│ g7 = function g(used1, used2) { //│ return (g$_arg) => { -//│ let used3; -//│ used3 = 2; -//│ return used1 + used2; +//│ return g$7(used1, used2, g$_arg); //│ }; //│ }; -//│ f3 = function f(used1, unused1) { +//│ f7 = function f(used1, unused1) { //│ let used2, unused2, foo, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ foo = g3(used1, used2) ?? null; +//│ foo = g7(used1, used2) ?? null; //│ tmp = foo(used2) ?? null; //│ return tmp + unused2; //│ }; -//│ f3(1, 2) +//│ f7(1, 2) //│ = 5 // don't touch g if not needed @@ -72,18 +126,18 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f5, g5; -//│ g5 = function g(g$_arg) { +//│ let f9, g9; +//│ g9 = function g(g$_arg) { //│ return g$_arg + 1; //│ }; -//│ f5 = function f(used1, unused1) { +//│ f9 = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g5(used2); +//│ tmp = g9(used2); //│ return tmp + unused2; //│ }; -//│ f5(1, 2) +//│ f9(1, 2) //│ = 5 @@ -96,24 +150,26 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f7, g7; -//│ g7 = function g(a1, a2, a3, a4, a5, a6) { +//│ let f11, g11, g$9; +//│ g$9 = function g$(a1, a2, a3, a4, a5, a6) { +//│ let tmp, tmp1, tmp2, tmp3; +//│ tmp = a1 + a2; +//│ tmp1 = tmp + a3; +//│ tmp2 = tmp1 + a4; +//│ tmp3 = tmp2 + a5; +//│ return tmp3 + a6; +//│ }; +//│ g11 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ let tmp, tmp1, tmp2, tmp3; -//│ tmp = a1 + a2; -//│ tmp1 = tmp + a3; -//│ tmp2 = tmp1 + a4; -//│ tmp3 = tmp2 + a5; -//│ return tmp3 + a6; +//│ return g$9(a1, a2, a3, a4, a5, a6); //│ }; //│ }; -//│ f7 = function f(a1, a2, a3, a4, a5, a6) { -//│ let tmp, g$this; -//│ g$this = g7(a1, a2, a3, a4, a5, a6) ?? null; -//│ tmp = g$this(); +//│ f11 = function f(a1, a2, a3, a4, a5, a6) { +//│ let tmp; +//│ tmp = g$9(a1, a2, a3, a4, a5, a6); //│ return tmp; //│ }; -//│ f7(1, 2, 3, 4, 5, 6) +//│ f11(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' @@ -132,16 +188,22 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f9, Tuple1, g9, h1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, f$capture1; -//│ g9 = function g(f$capture2) { +//│ let f13, Tuple1, g13, h1, ret, f14, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$1, g$11, f$capture1; +//│ g$11 = function g$(f$capture2) { +//│ f$capture2.a0$ = 1; +//│ return null; +//│ }; +//│ g13 = function g(f$capture2) { //│ return () => { -//│ f$capture2.a0$ = 1; -//│ return null; +//│ return g$11(f$capture2); //│ }; //│ }; +//│ h$1 = function h$(f$capture2) { +//│ return f$capture2.a0$; +//│ }; //│ h1 = function h(f$capture2) { //│ return () => { -//│ return f$capture2.a0$; +//│ return h$1(f$capture2); //│ }; //│ }; //│ f$capture1 = function f$capture(a0$1) { @@ -153,11 +215,11 @@ x + y //│ } //│ toString() { return "f$capture(" + this.a0$ + ")"; } //│ }; -//│ f9 = function f() { +//│ f13 = function f() { //│ let capture, g$this, h$this; //│ capture = new f$capture1(null); //│ capture.a0$ = 0; -//│ g$this = g9(capture) ?? null; +//│ g$this = g13(capture) ?? null; //│ h$this = h1(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; @@ -171,14 +233,14 @@ x + y //│ } //│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } //│ }; -//│ tmp = f9(); +//│ tmp = f13(); //│ ret = tmp; -//│ f11 = ret.a; +//│ f14 = ret.a; //│ f21 = ret.b; //│ tmp1 = f21() ?? null; //│ tmp2 = tmp1.toString() ?? null; //│ x = tmp2; -//│ tmp3 = f11() ?? null; +//│ tmp3 = f14() ?? null; //│ tmp4 = f21() ?? null; //│ tmp5 = tmp4.toString() ?? null; //│ y = tmp5; @@ -239,16 +301,22 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f14, g13, h5, f$capture5; -//│ g13 = function g(immutable, f$capture6) { +//│ let f18, g17, h5, h$5, g$13, f$capture5; +//│ g$13 = function g$(immutable, f$capture6) { +//│ f$capture6.mutated0$ = 2; +//│ return immutable + f$capture6.mutated0$; +//│ }; +//│ g17 = function g(immutable, f$capture6) { //│ return () => { -//│ f$capture6.mutated0$ = 2; -//│ return immutable + f$capture6.mutated0$; +//│ return g$13(immutable, f$capture6); //│ }; //│ }; +//│ h$5 = function h$(immutable, f$capture6) { +//│ return f$capture6.mutated0$; +//│ }; //│ h5 = function h(immutable, f$capture6) { //│ return () => { -//│ return f$capture6.mutated0$; +//│ return h$5(immutable, f$capture6); //│ }; //│ }; //│ f$capture5 = function f$capture(mutated0$1) { @@ -260,18 +328,16 @@ f(1, 2, 1000) //│ } //│ toString() { return "f$capture(" + this.mutated0$ + ")"; } //│ }; -//│ f14 = function f(unused, immutable, mutated) { -//│ let a1, tmp21, tmp22, tmp23, capture, g$this, h$this; +//│ f18 = function f(unused, immutable, mutated) { +//│ let a1, tmp21, tmp22, tmp23, capture; //│ capture = new f$capture5(mutated); -//│ g$this = g13(immutable, capture) ?? null; -//│ tmp21 = g$this(); +//│ tmp21 = g$13(immutable, capture); //│ a1 = tmp21; -//│ h$this = h5(immutable, capture) ?? null; -//│ tmp22 = h$this(); +//│ tmp22 = h$5(immutable, capture); //│ tmp23 = a1 + tmp22; //│ return tmp23 + unused; //│ }; -//│ f14(1, 2, 1000) +//│ f18(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -303,39 +369,41 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let f20, g19, h7; -//│ g19 = function g(arg) { +//│ let f25, g25, h7, h$7, g$19; +//│ g$19 = function g$(arg, n) { +//│ let scrut, tmp21; +//│ scrut = n <= 0; +//│ if (scrut === true) { +//│ return arg; +//│ } else { +//│ tmp21 = n - 1; +//│ return h$7(arg, tmp21); +//│ } +//│ }; +//│ g25 = function g(arg) { //│ return (n) => { -//│ let scrut, tmp21, h$this; -//│ scrut = n <= 0; -//│ if (scrut === true) { -//│ return arg; -//│ } else { -//│ tmp21 = n - 1; -//│ h$this = h7(arg) ?? null; -//│ return h$this(tmp21); -//│ } +//│ return g$19(arg, n); //│ }; //│ }; +//│ h$7 = function h$(arg, n) { +//│ let scrut, tmp21; +//│ scrut = n <= 0; +//│ if (scrut === true) { +//│ return arg; +//│ } else { +//│ tmp21 = n - 1; +//│ return g$19(arg, tmp21); +//│ } +//│ }; //│ h7 = function h(arg) { //│ return (n) => { -//│ let scrut, tmp21, g$this; -//│ scrut = n <= 0; -//│ if (scrut === true) { -//│ return arg; -//│ } else { -//│ tmp21 = n - 1; -//│ g$this = g19(arg) ?? null; -//│ return g$this(tmp21); -//│ } +//│ return h$7(arg, n); //│ }; //│ }; -//│ f20 = function f(arg) { -//│ let h$this; -//│ h$this = h7(arg) ?? null; -//│ return h$this(5); +//│ f25 = function f(arg) { +//│ return h$7(arg, 5); //│ }; -//│ f20(2) +//│ f25(2) //│ = 2 :expect 1 @@ -350,6 +418,7 @@ f() /// CLASSES /// :expect 1 +:sjs fun f(used1, unused1) = fun g(g_arg) = let used3 = 2 @@ -360,6 +429,78 @@ fun f(used1, unused1) = fun get() = used1 Test(unused1) f(1, 2).get() +//│ JS (unsanitized): +//│ let f31, Test1, g29, h9, tmp21, Test$ctor1, Test$1, g$23, h$9; +//│ h$9 = function h$(used3, used1) { +//│ return used3; +//│ }; +//│ h9 = function h(used3, used1) { +//│ return () => { +//│ return h$9(used3, used1); +//│ }; +//│ }; +//│ g$23 = function g$(used1, g$_arg) { +//│ let used3, tmp22; +//│ used3 = 2; +//│ tmp22 = h$9(used3, used1); +//│ return used1 + tmp22; +//│ }; +//│ g29 = function g(used1) { +//│ return (g$_arg) => { +//│ return g$23(used1, g$_arg); +//│ }; +//│ }; +//│ Test$1 = function Test$(used1, a1) { +//│ let tmp22; +//│ tmp22 = new Test1(a1); +//│ return tmp22(used1); +//│ }; +//│ Test$ctor1 = function Test$ctor(used1) { +//│ return (a1) => { +//│ let tmp22; +//│ tmp22 = new Test1(a1); +//│ return tmp22(used1); +//│ }; +//│ }; +//│ Test1 = function Test(a2) { +//│ return (used11) => { +//│ return new Test.class(a2)(used11); +//│ } +//│ }; +//│ Test1.class = class Test { +//│ constructor(a1) { +//│ return (used1) => { +//│ this.a = a1; +//│ this.used1 = used1; +//│ return this; +//│ } +//│ } +//│ get() { +//│ return this.used1; +//│ } +//│ toString() { return "Test(" + this.a + ")"; } +//│ }; +//│ f31 = function f(used1, unused1) { +//│ let unused2; +//│ unused2 = 2; +//│ return Test$1(used1, unused1); +//│ }; +//│ tmp21 = f31(1, 2); +//│ tmp21.get() ?? null +//│ = 1 + +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + let foo = Test + foo(unused1) +f(1, 2).get() //│ = 1 :fixme @@ -377,61 +518,15 @@ f(1, 2).get() //│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor :expect 2 -:sjs fun f(x) = class A() with fun f() = set x = 2 A().f() x f(1) -//│ JS (unsanitized): -//│ let f31, A1, A$ctor1, f$capture9; -//│ A$ctor1 = function A$ctor(f$capture10) { -//│ return () => { -//│ let tmp23; -//│ tmp23 = new A1(); -//│ return tmp23(f$capture10); -//│ }; -//│ }; -//│ A1 = function A() { -//│ return (f$capture11) => { -//│ return new A.class()(f$capture11); -//│ } -//│ }; -//│ A1.class = class A { -//│ constructor() { -//│ return (f$capture10) => { -//│ this.f$capture = f$capture10; -//│ return this; -//│ } -//│ } -//│ f() { -//│ this.f$capture.x0$ = 2; -//│ return null; -//│ } -//│ toString() { return "A(" + + ")"; } -//│ }; -//│ f$capture9 = function f$capture(x0$1) { -//│ return new f$capture.class(x0$1); -//│ }; -//│ f$capture9.class = class f$capture { -//│ constructor(x0$) { -//│ this.x0$ = x0$; -//│ } -//│ toString() { return "f$capture(" + this.x0$ + ")"; } -//│ }; -//│ f31 = function f(x1) { -//│ let tmp23, tmp24, capture, A$this; -//│ capture = new f$capture9(x1); -//│ A$this = A$ctor1(capture) ?? null; -//│ tmp23 = A$this(); -//│ tmp24 = tmp23.f() ?? null; -//│ return capture.x0$; -//│ }; -//│ f31(1) //│ = 2 -:sjs +:expect 6 class A(a) with fun f(b) = fun g(c) = @@ -439,42 +534,6 @@ class A(a) with a + b + c g A(1).f(1)(3) -//│ JS (unsanitized): -//│ let A3, g29, tmp23, tmp24, f$capture11; -//│ g29 = function g(A$instance, f$capture12) { -//│ return (c1) => { -//│ let tmp25; -//│ f$capture12.b0$ = 2; -//│ tmp25 = A$instance.a + f$capture12.b0$; -//│ return tmp25 + c1; -//│ }; -//│ }; -//│ f$capture11 = function f$capture(b0$1) { -//│ return new f$capture.class(b0$1); -//│ }; -//│ f$capture11.class = class f$capture { -//│ constructor(b0$) { -//│ this.b0$ = b0$; -//│ } -//│ toString() { return "f$capture(" + this.b0$ + ")"; } -//│ }; -//│ A3 = function A(a2) { -//│ return new A.class(a2); -//│ }; -//│ A3.class = class A { -//│ constructor(a1) { -//│ this.a = a1; -//│ } -//│ f(b1) { -//│ let capture; -//│ capture = new f$capture11(b1); -//│ return g29(this, capture) ?? null; -//│ } -//│ toString() { return "A(" + this.a + ")"; } -//│ }; -//│ tmp23 = A3(1); -//│ tmp24 = tmp23.f(1) ?? null; -//│ tmp24(3) ?? null //│ = 6 // Here, g does not need the capture. This should be optimized @@ -489,16 +548,22 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f34, f35, g31, f$capture13; -//│ g31 = function g(f$capture14) { +//│ let f40, f41, g37, f$3, g$31, f$capture13; +//│ g$31 = function g$(f$capture14) { +//│ return 1; +//│ }; +//│ g37 = function g(f$capture14) { //│ return () => { -//│ return 1; +//│ return g$31(f$capture14); //│ }; //│ }; -//│ f35 = function f(f$capture14) { +//│ f$3 = function f$(f$capture14) { +//│ f$capture14.x0$ = 1; +//│ return null; +//│ }; +//│ f41 = function f(f$capture14) { //│ return () => { -//│ f$capture14.x0$ = 1; -//│ return null; +//│ return f$3(f$capture14); //│ }; //│ }; //│ f$capture13 = function f$capture(x0$1) { @@ -510,15 +575,14 @@ f() //│ } //│ toString() { return "f$capture(" + this.x0$ + ")"; } //│ }; -//│ f34 = function f() { -//│ let tmp25, capture, f$this; +//│ f40 = function f() { +//│ let tmp26, capture; //│ capture = new f$capture13(null); //│ capture.x0$ = 1; -//│ f$this = f35(capture) ?? null; -//│ tmp25 = f$this(); +//│ tmp26 = f$3(capture); //│ return capture.x0$; //│ }; -//│ f34() +//│ f40() //│ = 1 // don't break private variables @@ -542,43 +606,6 @@ class A(a) with A(2).f() //│ ═══[RUNTIME ERROR] Expected: 2, got: null -:lot -:sjs -class B() -//│ JS (unsanitized): -//│ let B1; -//│ B1 = function B() { -//│ return new B.class(); -//│ }; -//│ B1.class = class B { -//│ constructor() {} -//│ toString() { return "B(" + + ")"; } -//│ }; -//│ null -//│ Lowered: -//│ Program: -//│ imports = Nil -//│ main = Define: -//│ defn = ClsLikeDefn: -//│ owner = N -//│ isym = class:B -//│ sym = member:B -//│ k = Cls -//│ paramsOpt = S of ParamList: -//│ flags = ParamListFlags of false -//│ params = Nil -//│ restParam = N -//│ auxParams = Nil -//│ parentPath = N -//│ methods = Nil -//│ privateFields = Nil -//│ publicFields = Nil -//│ preCtor = End of "" -//│ ctor = End of "" -//│ rest = Return: \ -//│ res = Lit of UnitLit of true -//│ implct = true - :sjs fun g() = let y = 0 @@ -595,12 +622,15 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let g35, f37, h13, tmp27, g$capture1, f$capture15; -//│ h13 = function h(f$capture16, g$capture2) { +//│ let g41, f43, h15, tmp28, f$5, h$15, g$capture1, f$capture15; +//│ h$15 = function h$(f$capture16, g$capture2) { +//│ f$capture16.k0$ = 5; +//│ f$capture16.x1$ = 4; +//│ return f$capture16.x1$ + g$capture2.y0$; +//│ }; +//│ h15 = function h(f$capture16, g$capture2) { //│ return () => { -//│ f$capture16.k0$ = 5; -//│ f$capture16.x1$ = 4; -//│ return f$capture16.x1$ + g$capture2.y0$; +//│ return h$15(f$capture16, g$capture2); //│ }; //│ }; //│ f$capture15 = function f$capture(k0$1, x1$1) { @@ -613,13 +643,16 @@ g()(1) //│ } //│ toString() { return "f$capture(" + this.k0$ + ", " + this.x1$ + ")"; } //│ }; -//│ f37 = function f(g$capture2) { +//│ f$5 = function f$(g$capture2, x1) { +//│ let capture; +//│ capture = new f$capture15(null, x1); +//│ capture.k0$ = 4; +//│ g$capture2.y0$ = 2; +//│ return capture.x1$; +//│ }; +//│ f43 = function f(g$capture2) { //│ return (x1) => { -//│ let capture; -//│ capture = new f$capture15(null, x1); -//│ capture.k0$ = 4; -//│ g$capture2.y0$ = 2; -//│ return capture.x1$; +//│ return f$5(g$capture2, x1); //│ }; //│ }; //│ g$capture1 = function g$capture(y0$1) { @@ -631,12 +664,12 @@ g()(1) //│ } //│ toString() { return "g$capture(" + this.y0$ + ")"; } //│ }; -//│ g35 = function g() { +//│ g41 = function g() { //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f37(capture) ?? null; +//│ return f43(capture) ?? null; //│ }; -//│ tmp27 = g35(); -//│ tmp27(1) ?? null +//│ tmp28 = g41(); +//│ tmp28(1) ?? null //│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 00e4348c1..c20094cde 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -158,11 +158,11 @@ if true do //│ JS (unsanitized): //│ let tmp11, handleBlock$23; //│ handleBlock$23 = function handleBlock$() { -//│ let h, scrut, f, tmp12, res, Cont$, Effect$h$; -//│ Effect$h$ = function Effect$h$() { -//│ return new Effect$h$.class(); +//│ let h, scrut, f, tmp12, res, Cont$, Handler$h$; +//│ Handler$h$ = function Handler$h$() { +//│ return new Handler$h$.class(); //│ }; -//│ Effect$h$.class = class Effect$h$ extends Effect1 { +//│ Handler$h$.class = class Handler$h$ extends Effect1 { //│ constructor() { //│ let tmp13; //│ tmp13 = super(); @@ -172,9 +172,9 @@ if true do //│ return arg; //│ }); //│ } -//│ toString() { return "Effect$h$(" + + ")"; } +//│ toString() { return "Handler$h$(" + + ")"; } //│ }; -//│ h = Effect$h$(); +//│ h = Handler$h$(); //│ Cont$ = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index 9b2799acc..241a93549 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -57,14 +57,14 @@ class Lol(h) with //│ }; //│ res = this.h.perform("k") ?? null; //│ if (res instanceof globalThis.Predef.__EffectSig.class) { -//│ res.tail.next = new Cont$.class(0); +//│ res.tail.next = Cont$(0); //│ res.tail = res.tail.next; //│ return res; //│ } //│ tmp = res; //│ res1 = Predef.print(tmp); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = new Cont$.class(1); +//│ res1.tail.next = Cont$(1); //│ res1.tail = res1.tail.next; //│ return res1; //│ } @@ -110,9 +110,9 @@ let oops = Lol(h) //│ > k //│ > b -//│ oops = Lol { h: Effect$h$ {} } +//│ oops = Lol { h: Handler$h$ {} } oops.h -//│ = Effect$h$ {} +//│ = Handler$h$ {} diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index fc79230c6..6e7412f62 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -47,10 +47,18 @@ handle h2 = Effect with //│ ║ l.38: then h2.perform(arg - 1) + " " + arg //│ ╙── ^^ //│ > ––– -//│ > performing 2 -//│ > ––– -//│ > performing 3 -//│ = [ undefined, undefined ] +//│ ═══[RUNTIME ERROR] Error: Unhandled effects + +// the above is due to a javascript quirk that should be addressed more generally: +:fixme +class A(hi) with + fun get(b) = hi + b +let x = new A(1) +let f = x.get // this strips the class of `this` for some reason +f(2) +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'hi') +//│ f = [Function: get] +//│ x = A { hi: 1 } // The current implementation insert new handlers surrounding the entire handle block, and hence "later" handle block become the outer one @@ -121,38 +129,41 @@ if true do h1.perform(()) str //│ JS (unsanitized): -//│ let str, scrut, tmp6, tmp7, handleBlock$9; +//│ let str, scrut, tmp8, tmp9, handleBlock$9; //│ str = ""; //│ scrut = true; //│ if (scrut === true) { //│ handleBlock$9 = function handleBlock$() { -//│ let h1, tmp8, handleBlock$10, Cont$, Effect$h1$; -//│ Effect$h1$ = class Effect$h1$ extends Effect1 { +//│ let h1, tmp10, handleBlock$10, Cont$, Handler$h1$; +//│ Handler$h1$ = function Handler$h1$() { +//│ return new Handler$h1$.class(); +//│ }; +//│ Handler$h1$.class = class Handler$h1$ extends Effect1 { //│ constructor() { -//│ let tmp9; -//│ tmp9 = super(); +//│ let tmp11; +//│ tmp11 = super(); //│ } //│ perform(arg) { -//│ return globalThis.Predef.__mkEffect(h1, (k) => { -//│ let tmp9, tmp10, tmp11, res5, Cont$1; +//│ return globalThis.Predef.__mkEffect(this, (k) => { +//│ let tmp11, tmp12, tmp13, res8, Cont$1; //│ Cont$1 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp12; -//│ tmp12 = super(null, null); +//│ let tmp14; +//│ tmp14 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 5) { -//│ res5 = value$; +//│ res8 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 5) { -//│ tmp10 = res5; -//│ tmp11 = str + "A"; -//│ str = tmp11; +//│ tmp12 = res8; +//│ tmp13 = str + "A"; +//│ str = tmp13; //│ return null; //│ } //│ break; @@ -160,45 +171,45 @@ str //│ } //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; -//│ tmp9 = str + "A"; -//│ str = tmp9; -//│ res5 = k(arg) ?? null; -//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { -//│ res5.tail.next = new Cont$1.class(5); -//│ res5.tail = res5.tail.next; -//│ return res5; -//│ } -//│ tmp10 = res5; //│ tmp11 = str + "A"; //│ str = tmp11; +//│ res8 = k(arg) ?? null; +//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { +//│ res8.tail.next = Cont$1(5); +//│ res8.tail = res8.tail.next; +//│ return res8; +//│ } +//│ tmp12 = res8; +//│ tmp13 = str + "A"; +//│ str = tmp13; //│ return null; //│ }); //│ } -//│ toString() { return "Effect$h1$"; } +//│ toString() { return "Handler$h1$(" + + ")"; } //│ }; -//│ h1 = new Effect$h1$(); +//│ h1 = Handler$h1$(); //│ Cont$ = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp9; -//│ tmp9 = super(null, null); +//│ let tmp11; +//│ tmp11 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 3) { -//│ tmp8 = value$; +//│ tmp10 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ if (tmp8 instanceof globalThis.Predef.__Return.class) { -//│ return tmp8; +//│ if (tmp10 instanceof globalThis.Predef.__Return.class) { +//│ return tmp10; //│ } //│ this.pc = 4; //│ continue contLoop; //│ } else if (this.pc === 4) { -//│ return tmp8; +//│ return tmp10; //│ } //│ break; //│ } @@ -206,34 +217,37 @@ str //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; //│ handleBlock$10 = function handleBlock$() { -//│ let h2, tmp9, res5, res6, Cont$1, Effect$h2$; -//│ Effect$h2$ = class Effect$h2$ extends Effect1 { +//│ let h2, tmp11, res8, res9, Cont$1, Handler$h2$; +//│ Handler$h2$ = function Handler$h2$() { +//│ return new Handler$h2$.class(); +//│ }; +//│ Handler$h2$.class = class Handler$h2$ extends Effect1 { //│ constructor() { -//│ let tmp10; -//│ tmp10 = super(); +//│ let tmp12; +//│ tmp12 = super(); //│ } //│ perform(arg) { -//│ return globalThis.Predef.__mkEffect(h2, (k) => { -//│ let tmp10, tmp11, tmp12, tmp13, tmp14, res7, Cont$2; +//│ return globalThis.Predef.__mkEffect(this, (k) => { +//│ let tmp12, tmp13, tmp14, tmp15, tmp16, res10, Cont$2; //│ Cont$2 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$2.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp15; -//│ tmp15 = super(null, null); +//│ let tmp17; +//│ tmp17 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 2) { -//│ res7 = value$; +//│ res10 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 2) { -//│ tmp12 = res7; -//│ tmp13 = str + "B"; -//│ tmp14 = str + tmp13; -//│ str = tmp14; +//│ tmp14 = res10; +//│ tmp15 = str + "B"; +//│ tmp16 = str + tmp15; +//│ str = tmp16; //│ return null; //│ } //│ break; @@ -241,88 +255,88 @@ str //│ } //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; -//│ tmp10 = str + "B"; -//│ tmp11 = str + tmp10; -//│ str = tmp11; -//│ res7 = k(arg) ?? null; -//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { -//│ res7.tail.next = new Cont$2.class(2); -//│ res7.tail = res7.tail.next; -//│ return res7; +//│ tmp12 = str + "B"; +//│ tmp13 = str + tmp12; +//│ str = tmp13; +//│ res10 = k(arg) ?? null; +//│ if (res10 instanceof globalThis.Predef.__EffectSig.class) { +//│ res10.tail.next = Cont$2(2); +//│ res10.tail = res10.tail.next; +//│ return res10; //│ } -//│ tmp12 = res7; -//│ tmp13 = str + "B"; -//│ tmp14 = str + tmp13; -//│ str = tmp14; +//│ tmp14 = res10; +//│ tmp15 = str + "B"; +//│ tmp16 = str + tmp15; +//│ str = tmp16; //│ return null; //│ }); //│ } -//│ toString() { return "Effect$h2$"; } +//│ toString() { return "Handler$h2$(" + + ")"; } //│ }; -//│ h2 = new Effect$h2$(); +//│ h2 = Handler$h2$(); //│ Cont$1 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp10; -//│ tmp10 = super(null, null); +//│ let tmp12; +//│ tmp12 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ res5 = value$; +//│ res8 = value$; //│ } else if (this.pc === 1) { -//│ res6 = value$; +//│ res9 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { -//│ tmp9 = res5; -//│ res6 = h1.perform(null) ?? null; -//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp11 = res8; +//│ res9 = h1.perform(null) ?? null; +//│ if (res9 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(res6, this); +//│ return globalThis.Predef.__appendInCont(res9, this); //│ } //│ this.pc = 1; //│ continue contLoop; //│ } else if (this.pc === 1) { -//│ return res6; +//│ return res9; //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + this.pc + ")"; } //│ }; -//│ res5 = h2.perform(null) ?? null; -//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { -//│ res5.tail.next = new Cont$1(0); -//│ return globalThis.Predef.__handleBlockImpl(res5, h2); +//│ res8 = h2.perform(null) ?? null; +//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { +//│ res8.tail.next = Cont$1(0); +//│ return globalThis.Predef.__handleBlockImpl(res8, h2); //│ } -//│ tmp9 = res5; -//│ res6 = h1.perform(null) ?? null; -//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { -//│ res6.tail.next = new Cont$1(1); -//│ return globalThis.Predef.__handleBlockImpl(res6, h2); +//│ tmp11 = res8; +//│ res9 = h1.perform(null) ?? null; +//│ if (res9 instanceof globalThis.Predef.__EffectSig.class) { +//│ res9.tail.next = Cont$1(1); +//│ return globalThis.Predef.__handleBlockImpl(res9, h2); //│ } -//│ return res6; +//│ return res9; //│ }; -//│ tmp8 = handleBlock$10(); -//│ if (tmp8 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp8.tail.next = new Cont$(3); -//│ return globalThis.Predef.__handleBlockImpl(tmp8, h1); +//│ tmp10 = handleBlock$10(); +//│ if (tmp10 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp10.tail.next = Cont$(3); +//│ return globalThis.Predef.__handleBlockImpl(tmp10, h1); //│ } -//│ if (tmp8 instanceof globalThis.Predef.__Return.class) { -//│ return tmp8; +//│ if (tmp10 instanceof globalThis.Predef.__Return.class) { +//│ return tmp10; //│ } -//│ return tmp8; +//│ return tmp10; //│ }; -//│ tmp6 = handleBlock$9(); -//│ if (tmp6 instanceof this.Predef.__EffectSig.class) { +//│ tmp8 = handleBlock$9(); +//│ if (tmp8 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ tmp7 = tmp6; +//│ tmp9 = tmp8; //│ } else { -//│ tmp7 = null; +//│ tmp9 = null; //│ } //│ str //│ = 'BABABA' diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index b62abd398..676fbf509 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -67,7 +67,7 @@ hi(0) //│ if (scrut1 === true) { //│ res1 = globalThis.Predef.__stackHandler.perform(); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = new Cont$.class(0); +//│ res1.tail.next = Cont$(0); //│ res1.tail = res1.tail.next; //│ return res1; //│ } @@ -84,13 +84,16 @@ hi(0) //│ }; //│ handleBlock$1 = function handleBlock$() { //│ let stackHandler, res1, Cont$, StackDelay$; -//│ StackDelay$ = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ StackDelay$ = function StackDelay$() { +//│ return new StackDelay$.class(); +//│ }; +//│ StackDelay$.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { //│ constructor() { //│ let tmp; //│ tmp = super(); //│ } //│ perform() { -//│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { +//│ return globalThis.Predef.__mkEffect(this, (resume) => { //│ let res2, res3, Cont$1; //│ Cont$1 = function Cont$(pc1) { //│ return new Cont$.class(pc1); @@ -118,7 +121,7 @@ hi(0) //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res3 = resume(); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = new Cont$1.class(4); +//│ res3.tail.next = Cont$1(4); //│ res3.tail = res3.tail.next; //│ return res3; //│ } @@ -126,9 +129,9 @@ hi(0) //│ return res2; //│ }); //│ } -//│ toString() { return "StackDelay$"; } +//│ toString() { return "StackDelay$(" + + ")"; } //│ }; -//│ stackHandler = new StackDelay$(); +//│ stackHandler = StackDelay$(); //│ Cont$ = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; @@ -157,7 +160,7 @@ hi(0) //│ globalThis.Predef.__stackHandler = stackHandler; //│ res1 = hi1(0); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = new Cont$(3); +//│ res1.tail.next = Cont$(3); //│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler); //│ } //│ return res1; @@ -251,7 +254,7 @@ sum(10000) //│ if (scrut1 === true) { //│ res2 = globalThis.Predef.__stackHandler.perform(); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = new Cont$.class(0); +//│ res2.tail.next = Cont$(0); //│ res2.tail = res2.tail.next; //│ return res2; //│ } @@ -266,7 +269,7 @@ sum(10000) //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; //│ res3 = sum3(tmp); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = new Cont$.class(1); +//│ res3.tail.next = Cont$(1); //│ res3.tail = res3.tail.next; //│ return res3; //│ } @@ -282,13 +285,16 @@ sum(10000) //│ }; //│ handleBlock$3 = function handleBlock$() { //│ let stackHandler, res2, Cont$, StackDelay$; -//│ StackDelay$ = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ StackDelay$ = function StackDelay$() { +//│ return new StackDelay$.class(); +//│ }; +//│ StackDelay$.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { //│ constructor() { //│ let tmp; //│ tmp = super(); //│ } //│ perform() { -//│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { +//│ return globalThis.Predef.__mkEffect(this, (resume) => { //│ let res3, res4, Cont$1; //│ Cont$1 = function Cont$(pc1) { //│ return new Cont$.class(pc1); @@ -316,7 +322,7 @@ sum(10000) //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res4 = resume(); //│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = new Cont$1.class(6); +//│ res4.tail.next = Cont$1(6); //│ res4.tail = res4.tail.next; //│ return res4; //│ } @@ -324,9 +330,9 @@ sum(10000) //│ return res3; //│ }); //│ } -//│ toString() { return "StackDelay$"; } +//│ toString() { return "StackDelay$(" + + ")"; } //│ }; -//│ stackHandler = new StackDelay$(); +//│ stackHandler = StackDelay$(); //│ Cont$ = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; @@ -355,7 +361,7 @@ sum(10000) //│ globalThis.Predef.__stackHandler = stackHandler; //│ res2 = sum3(10000); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = new Cont$(5); +//│ res2.tail.next = Cont$(5); //│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler); //│ } //│ return res2; From 3e048545035ec5432b044d93d96f55fb23baec7a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 31 Jan 2025 16:25:29 +0800 Subject: [PATCH 032/127] Merge hkmc2 --- .../src/main/scala/hkmc2/MLsCompiler.scala | 3 +- .../src/main/scala/hkmc2/codegen/Block.scala | 41 ++- .../scala/hkmc2/codegen/HandlerLowering.scala | 3 +- .../main/scala/hkmc2/codegen/Lowering.scala | 14 +- .../scala/hkmc2/codegen/js/JSBuilder.scala | 87 ++++-- .../scala/hkmc2/semantics/Elaborator.scala | 13 +- .../main/scala/hkmc2/semantics/Symbol.scala | 20 +- .../src/main/scala/hkmc2/semantics/Term.scala | 24 +- .../src/main/scala/hkmc2/syntax/Parser.scala | 7 +- .../src/main/scala/hkmc2/syntax/Tree.scala | 11 +- .../src/main/scala/hkmc2/utils/Scope.scala | 5 +- .../src/test/mlscript-compile/Example.mjs | 15 +- .../src/test/mlscript-compile/Option.mjs | 21 +- .../src/test/mlscript-compile/Predef.mjs | 250 +++++++++++------- .../src/test/mlscript-compile/Predef.mls | 26 +- .../src/test/mlscript-compile/Stack.mjs | 72 +++-- .../shared/src/test/mlscript-compile/Str.mjs | 15 +- .../test/mlscript-compile/apps/Accounting.mjs | 16 +- .../src/test/mlscript-compile/apps/CSV.mjs | 2 +- .../src/test/mlscript/backlog/ToTriage.mls | 42 ++- .../shared/src/test/mlscript/backlog/UCS.mls | 2 +- .../src/test/mlscript/basics/BadDefs.mls | 10 +- .../test/mlscript/basics/BadModuleUses.mls | 14 +- .../test/mlscript/basics/DynamicFields.mls | 66 +++++ .../src/test/mlscript/basics/Indentation.mls | 2 +- .../mlscript/basics/MemberProjections.mls | 4 +- .../test/mlscript/basics/MiscArrayTests.mls | 6 +- .../test/mlscript/basics/ModuleMethods.mls | 8 +- .../test/mlscript/basics/MultiParamLists.mls | 20 +- .../src/test/mlscript/basics/MutVal.mls | 2 +- .../src/test/mlscript/basics/OpenIn.mls | 8 +- .../src/test/mlscript/basics/Overloading.mls | 4 +- .../src/test/mlscript/bbml/bbCodeGen.mls | 26 +- .../src/test/mlscript/bbml/bbGetters.mls | 4 +- .../src/test/mlscript/codegen/Arrays.mls | 2 +- .../src/test/mlscript/codegen/BadInit.mls | 17 +- .../src/test/mlscript/codegen/BuiltinOps.mls | 2 +- .../src/test/mlscript/codegen/CaseOfCase.mls | 12 +- .../test/mlscript/codegen/CaseShorthand.mls | 6 +- .../test/mlscript/codegen/ClassInClass.mls | 14 +- .../src/test/mlscript/codegen/ClassInFun.mls | 26 +- .../test/mlscript/codegen/ClassMatching.mls | 18 +- .../src/test/mlscript/codegen/Comma.mls | 6 +- .../test/mlscript/codegen/DelayedLetInit.mls | 16 +- .../src/test/mlscript/codegen/EarlyReturn.mls | 6 +- .../src/test/mlscript/codegen/FunInClass.mls | 46 ++-- .../src/test/mlscript/codegen/Functions.mls | 2 +- .../test/mlscript/codegen/FunctionsThis.mls | 6 +- .../src/test/mlscript/codegen/Getters.mls | 64 +++-- .../src/test/mlscript/codegen/GlobalThis.mls | 6 +- .../src/test/mlscript/codegen/Hygiene.mls | 70 ++++- .../src/test/mlscript/codegen/IfThenElse.mls | 6 +- .../src/test/mlscript/codegen/ImportMLsJS.mls | 5 +- .../src/test/mlscript/codegen/ImportedOps.mls | 6 +- .../src/test/mlscript/codegen/Lambdas.mls | 2 +- .../src/test/mlscript/codegen/Lifter.mls | 246 ++++++++--------- .../src/test/mlscript/codegen/Modules.mls | 39 +-- .../codegen/ObjectMethodDebinding.mls | 47 ++++ .../test/mlscript/codegen/OpenWildcard.mls | 4 +- .../src/test/mlscript/codegen/OptMatch.mls | 8 +- .../test/mlscript/codegen/ParamClasses.mls | 40 +-- .../src/test/mlscript/codegen/PartialApps.mls | 6 +- .../test/mlscript/codegen/PlainClasses.mls | 99 ++++--- .../src/test/mlscript/codegen/PredefUsage.mls | 3 + .../src/test/mlscript/codegen/RandomStuff.mls | 96 +++++++ .../shared/src/test/mlscript/codegen/Repl.mls | 6 +- .../test/mlscript/codegen/SanityChecks.mls | 41 ++- .../src/test/mlscript/codegen/SetIn.mls | 42 +-- .../src/test/mlscript/codegen/Spreads.mls | 2 +- .../shared/src/test/mlscript/codegen/This.mls | 21 +- .../mlscript/codegen/ThisCallVariations.mls | 4 +- .../src/test/mlscript/codegen/ThisCalls.mls | 34 ++- .../src/test/mlscript/codegen/Throw.mls | 8 +- .../src/test/mlscript/codegen/TraceLog.mls | 8 +- .../test/mlscript/codegen/TraceLogIndent.mls | 4 +- .../src/test/mlscript/codegen/While.mls | 10 +- .../src/test/mlscript/decls/Prelude.mls | 2 + .../src/test/mlscript/handlers/Effects.mls | 24 +- .../test/mlscript/handlers/EffectsHygiene.mls | 28 +- .../mlscript/handlers/EffectsInClasses.mls | 18 +- .../src/test/mlscript/handlers/Generators.mls | 12 +- .../mlscript/handlers/HandlersInClasses.mls | 2 +- .../mlscript/handlers/RecursiveHandlers.mls | 68 ++--- .../test/mlscript/handlers/StackSafety.mls | 115 ++++---- .../src/test/mlscript/meta/ImporterTest.mls | 4 +- .../src/test/mlscript/rp/LocalPatterns.mls | 5 +- .../src/test/mlscript/rp/MatchResult.mls | 4 +- .../mlscript/{basics => std}/PredefTest.mls | 8 + .../src/test/mlscript/std/Rendering.mls | 78 ++++++ .../src/test/mlscript/std/StackTests.mls | 2 +- .../test/mlscript/syntax/KeywordStutters.mls | 4 +- .../syntax/annotations/Declarations.mls | 2 +- .../ucs/normalization/SimplePairMatches.mls | 4 +- .../test/mlscript/ucs/patterns/RestTuple.mls | 12 +- .../src/test/scala/hkmc2/DiffMaker.scala | 1 + .../test/scala/hkmc2/JSBackendDiffMaker.scala | 21 +- 96 files changed, 1509 insertions(+), 874 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls create mode 100644 hkmc2/shared/src/test/mlscript/codegen/ObjectMethodDebinding.mls create mode 100644 hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls rename hkmc2/shared/src/test/mlscript/{basics => std}/PredefTest.mls (93%) create mode 100644 hkmc2/shared/src/test/mlscript/std/Rendering.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala index ac490c4e6..7cf905c3b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala @@ -75,7 +75,8 @@ class MLsCompiler(preludeFile: os.Path): val (blk, newCtx) = elab.importFrom(parsed) val low = ltl.givenIn: codegen.Lowering(lowerHandlers = false, stackLimit = None, lift = false) // TODO: properly hook up stack limit - val jsb = codegen.js.JSBuilder() + val jsb = ltl.givenIn: + codegen.js.JSBuilder() val le = low.program(blk) val baseScp: utils.Scope = utils.Scope.empty diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 049fd18b9..a37be7c88 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -30,6 +30,7 @@ sealed abstract class Block extends Product with AutoLocated: case Assign(l: TermSymbol, r, rst) => rst.definedVars case Assign(l, r, rst) => rst.definedVars + l case AssignField(l, n, r, rst) => rst.definedVars + case AssignDynField(l, n, ai, r, rst) => rst.definedVars case Match(scrut, arms, dflt, rst) => arms.flatMap(_._2.definedVars).toSet ++ dflt.toList.flatMap(_.definedVars) ++ rst.definedVars case End(_) => Set.empty @@ -89,13 +90,20 @@ sealed abstract class Block extends Product with AutoLocated: case End(msg) => Set.empty lazy val subBlocks: Ls[Block] = this match - case Match(_, arms, dflt, rest) => arms.map(_._2) ++ dflt.toList :+ rest + case Match(p, arms, dflt, rest) => p.subBlocks ++ arms.map(_._2) ++ dflt.toList :+ rest case Begin(sub, rest) => sub :: rest :: Nil case TryBlock(sub, finallyDo, rest) => sub :: finallyDo :: rest :: Nil - case Assign(_, rhs, rest) => rest :: Nil - case AssignField(_, _, rhs, rest) => rest :: Nil - case Define(_, rest) => rest :: Nil - case HandleBlock(_, _, _, _, handlers, body, rest) => handlers.map(_.body) :+ body :+ rest + case Assign(_, rhs, rest) => rhs.subBlocks ::: rest :: Nil + case AssignField(_, _, rhs, rest) => rhs.subBlocks ::: rest :: Nil + case AssignDynField(_, _, _, rhs, rest) => rhs.subBlocks ::: rest :: Nil + case Define(d, rest) => d.subBlocks ::: rest :: Nil + case HandleBlock(_, _, par, _, handlers, body, rest) => par.subBlocks ++ handlers.map(_.body) :+ body :+ rest + + // TODO rm Lam from values and thus the need for these cases + case Return(r, _) => r.subBlocks + case HandleBlockReturn(r) => r.subBlocks + case Throw(r) => r.subBlocks + case _: Return | _: Throw | _: Label | _: Break | _: Continue | _: End | _: HandleBlockReturn => Nil // Moves definitions in a block to the top. Only scans the top-level definitions of the block; @@ -150,6 +158,8 @@ case class Assign(lhs: Local, rhs: Result, rest: Block) extends Block with Produ case class AssignField(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block)(val symbol: Opt[FieldSymbol]) extends Block with ProductWithTail +case class AssignDynField(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, rest: Block) extends Block with ProductWithTail + case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail case class HandleBlock( @@ -169,7 +179,13 @@ sealed abstract class Defn: val sym: BlockMemberSymbol def isOwned: Bool = owner.isDefined def owner: Opt[InnerSymbol] - + + def subBlocks: Ls[Block] = this match + case FunDefn(body = body) => body :: Nil + case _: ValDefn => Nil + case ClsLikeDefn(preCtor = preCtor, ctor = ctor, methods = mtds) => + preCtor :: ctor :: mtds.flatMap(_.subBlocks) + lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars @@ -234,7 +250,16 @@ enum Case: case Tup(_, _) => Set.empty sealed abstract class Result: - + + // TODO rm Lam from values and thus the need for this method + def subBlocks: Ls[Block] = this match + case Call(fun, args) => fun.subBlocks ::: args.flatMap(_.value.subBlocks) + case Instantiate(cls, args) => args.flatMap(_.subBlocks) + case Select(qual, name) => qual.subBlocks + case Value.Lam(params, body) => body :: Nil + case Value.Arr(elems) => elems.flatMap(_.value.subBlocks) + case _ => Nil + lazy val freeVars: Set[Local] = this match case Call(fun, args) => args.flatMap(_.value.freeVars).toSet case Instantiate(cls, args) => args.flatMap(_.freeVars).toSet @@ -259,6 +284,8 @@ sealed abstract class Path extends Result: case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[FieldSymbol]) extends Path with ProductWithExtraInfo: def extraInfo: Str = symbol.mkString +case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool) extends Path + enum Value extends Path: case Ref(l: Local) case This(sym: InnerSymbol) // TODO rm – just use Ref diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 6dc95cda9..30b443810 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -346,7 +346,8 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): FunDefn(f.owner, f.sym, f.params, translateBlock(f.body, functionHandlerCtx)) private def translateCls(cls: ClsLikeDefn): ClsLikeDefn = - cls.copy(methods = cls.methods.map(translateFun), ctor = translateBlock(cls.ctor, ctorCtx(cls.sym.asClsLike.get.asPath))) + cls.copy(methods = cls.methods.map(translateFun), + ctor = translateBlock(cls.ctor, ctorCtx(cls.sym.asClsLike.getOrElse(wat("asClsLike", cls.sym)).asPath))) // Handle block becomes a FunDefn and CallPlaceholder private def translateHandleBlock(h: HandleBlock): Block = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 4f5a124d2..c12cf9e52 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -299,6 +299,11 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int], lift: Bool)(using T subTerm(prefix): p => subTerm_nonTail(rhs): r => AssignField(p, nme, r, k(Value.Lit(syntax.Tree.UnitLit(true))))(sel.sym) + case sel @ DynSel(prefix, fld, ai) => + subTerm(prefix): p => + subTerm_nonTail(fld): f => + subTerm_nonTail(rhs): r => + AssignDynField(p, f, ai, r, k(Value.Lit(syntax.Tree.UnitLit(true)))) case st.Blk((imp @ Import(sym, path)) :: stats, res) => raise(ErrorReport( @@ -421,12 +426,17 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int], lift: Bool)(using T else k(Value.Lit(syntax.Tree.UnitLit(true))) // * it seems this currently never happens ) + case sel @ Sel(prefix, nme) => + setupSelection(prefix, nme, sel.sym)(k) + case sel @ SynthSel(prefix, nme) => subTerm(prefix): p => k(Select(p, nme)(sel.sym)) - case sel @ Sel(prefix, nme) => - setupSelection(prefix, nme, sel.sym)(k) + case DynSel(prefix, fld, ai) => + subTerm(prefix): p => + subTerm_nonTail(fld): f => + k(DynSelect(p, f, ai)) case New(cls, as) => 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 9769ac5fd..05c0beb44 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -9,6 +9,7 @@ import document.* import hkmc2.Message.MessageContext import hkmc2.syntax.{Tree, ImmutVal} import hkmc2.semantics.* +import Elaborator.{State, Ctx} import hkmc2.codegen.Value.Lam import Scope.scope @@ -20,7 +21,7 @@ abstract class CodeBuilder: type Context -class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: +class JSBuilder(using TL, State, Ctx) extends CodeBuilder: val builtinOpsBase: Ls[Str] = Ls( "+", "-", "*", "/", "%", @@ -64,7 +65,11 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: }" case N => summon[Scope].lookup_!(ts) case ts: semantics.InnerSymbol => - summon[Scope].findThis_!(ts) + if ts.asMod.isDefined + then + // * Module self-references use the module name itself instead of `this` + summon[Scope].lookup_!(ts) + else summon[Scope].findThis_!(ts) case _ => summon[Scope].lookup_!(l) def argument(a: Arg)(using Raise, Scope): Document = @@ -124,6 +129,8 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: case S(index) => s"[$index]" case N => s"[${JSBuilder.makeStringLiteral(name)}]" }" + case DynSelect(qual, fld, ai) => + doc"${result(qual)}[${result(fld)}]" case Instantiate(cls, as) => doc"new ${result(cls)}(${as.map(result).mkDocument(", ")})" case Value.Arr(es) if es.isEmpty => doc"[]" @@ -136,6 +143,8 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: doc" # ${getVar(l)} = ${result(r)};${returningTerm(rst)}" case AssignField(p, n, r, rst) => doc" # ${result(p)}.${n.name} = ${result(r)};${returningTerm(rst)}" + case AssignDynField(p, f, ai, r, rst) => + doc" # ${result(p)}[${result(f)}] = ${result(r)};${returningTerm(rst)}" case Define(defn, rst) => def mkThis(sym: InnerSymbol): Document = result(Value.This(sym)) @@ -167,11 +176,15 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val ctorParams = clsParams.map(p => p -> scope.allocateName(p)) val ctorAuxParams = auxParams.map(ps => ps.params.map(p => p.sym -> scope.allocateName(p.sym))) + + val isModule = kind is syntax.Mod + val mtdPrefix = if isModule then "static " else "" + val privs = val scp = isym.asInstanceOf[InnerSymbol].privatesScope privFlds.map: fld => val nme = scp.allocateName(fld) - doc" # #$nme;" + doc" # $mtdPrefix#$nme;" .mkDocument(doc"") val preCtorCode = (ctorParams ++ ctorAuxParams.flatMap(ps => ps)).foldLeft(body(preCtor)): case (acc, (sym, nme)) => @@ -189,45 +202,52 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: doc"${ braced(doc" # return $funBod") }" - val clsJS = doc"class ${sym.nme}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ + val ctorOrStatic = if isModule + then doc"static" + else doc"constructor(${ + ctorParams.unzip._2.mkDocument(", ") + })" + val clsJS = doc"class ${scope.lookup_!(isym)}${par.map(p => s" extends ${result(p)}").getOrElse("")} { #{ ${ privs - } # constructor(${ - ctorParams.unzip._2.mkDocument(", ") - }) $ctorBod${ + } # $ctorOrStatic $ctorBod${ mtds.map: case td @ FunDefn(_, _, ps :: pss, bod) => val result = pss.foldRight(bod): case (ps, block) => Return(Lam(ps, block), false) val (params, bodyDoc) = setupFunction(some(td.sym.nme), ps, result) - doc" # ${td.sym.nme}($params) ${ braced(bodyDoc) }" + doc" # $mtdPrefix${td.sym.nme}($params) ${ braced(bodyDoc) }" case td @ FunDefn(_, _, Nil, bod) => - doc" # get ${td.sym.nme}() ${ braced(body(bod)) }" + doc" # ${mtdPrefix}get ${td.sym.nme}() ${ braced(body(bod)) }" .mkDocument(" ") }${ if mtds.exists(_.sym.nme == "toString") then doc"" - else doc""" # toString() { return "${sym.nme}${ + else doc""" # ${mtdPrefix}toString() { return "${sym.nme}${ if paramsOpt.isEmpty then doc""""""" else doc"""(" + ${ - ctorParams.headOption.fold("")("this." + _._1.name) + ctorParams.headOption.fold("\"\"")("globalThis.Predef.render(this." + _._1.name + ")") }${ ctorParams.tailOption.fold("")(_.map( - """ + ", " + this.""" + _._1.name).mkString) + """ + ", " + globalThis.Predef.render(this.""" + _._1.name + ")").mkString) } + ")"""" }; }""" } #} # }" if (kind is syntax.Mod) || (kind is syntax.Obj) || (kind is syntax.Pat) then - val clsTmp = outerScope.allocateName(new semantics.TempSymbol(N, sym.nme+"$class")) + lazy val clsTmp = outerScope.allocateName(new semantics.TempSymbol(N, sym.nme+"$class")) ownr match case S(owner) => assert((kind is syntax.Pat) || paramsOpt.isEmpty) // doc"${mkThis(owner)}.${sym.nme} = new ${clsJS}" - doc"const $clsTmp = ${clsJS}; # ${mkThis(owner)}.${sym.nme} = new ${clsTmp + if isModule + then doc"${mkThis(owner)}.${sym.nme} = ${clsJS};" + else doc"const $clsTmp = ${clsJS}; # ${mkThis(owner)}.${sym.nme} = new ${clsTmp }; # ${mkThis(owner)}.${sym.nme}.class = $clsTmp;" case N => val v = getVar(sym) - doc"const $clsTmp = ${clsJS}; ${v} = new ${clsTmp + if isModule + then doc"${v} = ${clsJS};" + else doc"const $clsTmp = ${clsJS}; ${v} = new ${clsTmp }; # ${v}.class = $clsTmp;" else val paramsAll = paramsOpt match @@ -327,16 +347,43 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: // case _ => ??? - def avoidNames(p: Program)(using Scope): Unit = - def go(blk: Block): Unit = blk match + /** We want to first reserve the names of all defined classes, object, and modules, + * as these will be used as the internal names for these things, which may differ from the external name. + * For instance, `class Foo() { ... Foo ... }` will essentially translate to + * `Foo1 = function Foo() { return new Foo1.class }; Foo1.class = class Foo { ... Foo1 ... }`. + * Here, we prefer the `class Foo` part to bear the original `Foo` name (and not, say, `Foo1` or `Foo2`), + * as it will be visible at JS runtime. + * Also note it is crucial here that the inner reference can access the outer definition `Foo1` and not `Foo` + * – Foo refers to the inner class in generated code and not to the parameterized Foo class of the source. + * For modules, we do turn any `this` reference into a reference to the corresponding generated class, + * since modules represent static members and since we want them to avoid the problem of JS method debinding. + * That means we must generate unique inner names, at least in the case of modules; + * for instance, consider that `module M with { val x = 1; module M with { val y = x } }` + * should generate something like `M2 = class M { static x = 1; static M1 = class M1 { static y = M.x } }`, + * where it is crucial that the inner module's inner name M1 not clash with the outer module's inner name M. + * We do not reserve the names of functions, as we currently just use the source name as the inner name, + * since any unintentional capture will have no consequence. + * For example, consider that `fun foo() = foo()` may generate something like + * `foo = function foo() { return foo(); }` + * or, if there was already a `foo` defined in some outer scope, + * `foo1 = function foo() { return foo1(); }` + * but the result has the same semantics. + * */ + def reserveNames(p: Program)(using Scope): Unit = + def go(blk: Block): Unit = tl.trace(s"avoidNames ${blk.toString.take(100)}..."): + blk match case Define(defn, rest) => - if !defn.isOwned then scope.allocateName(semantics.TempSymbol(N, defn.sym.nme)) + defn match + case d: ClsLikeDefn => + scope.allocateName(d.isym) + case _ => //scope.allocateName(defn.sym) + defn.subBlocks.foreach(go) go(rest) case _ => blk.subBlocks.foreach(go) go(p.main) def program(p: Program, exprt: Opt[BlockMemberSymbol], wd: os.Path)(using Raise, Scope): Document = - avoidNames(p) + reserveNames(p) p.imports.foreach: i => i._1 -> scope.allocateName(i._1) val imps = p.imports.map: i => @@ -352,7 +399,7 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder: ) def worksheet(p: Program)(using Raise, Scope): (Document, Document) = - avoidNames(p) + reserveNames(p) p.imports.foreach: i => i._1 -> scope.allocateName(i._1) val imps = p.imports.map: i => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index a41293e56..75be8e902 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -159,7 +159,7 @@ extends Importer: def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[FieldSymbol] = base match case S(psym: BlockMemberSymbol) => - psym.modTree match + psym.modOrObjTree match case S(cls) => cls.definedSymbols.get(nme.name) match case s @ S(clsSym) => s @@ -186,7 +186,7 @@ extends Importer: trm case S(mem: BlockMemberSymbol) => if !mem.hasLiftedClass then trm - else Term.SynthSel(trm, Ident("class"))(mem.clsTree.orElse(mem.modTree).map(_.symbol)) + else Term.SynthSel(trm, Ident("class"))(mem.clsTree.orElse(mem.modOrObjTree).map(_.symbol)) case _ => trm def annot(tree: Tree): Ctxl[Opt[Annot]] = tree match @@ -549,6 +549,8 @@ extends Importer: Term.Error case OpenIn(op, body) => term(Block(Open(op) :: body :: Nil), inAppPrefix) + case DynAccess(obj, fld, ai) => + Term.DynSel(term(obj), term(fld), ai) case Spread(kw, kwLoc, body) => raise(ErrorReport(msg"Illegal position for '${kw.name}' spread operator." -> tree.toLoc :: Nil)) Term.Error @@ -656,8 +658,8 @@ extends Importer: val importedNames = importedTrees match case N => // "wilcard" open baseElem.symbol match - case S(sym: BlockMemberSymbol) if sym.modTree.isDefined => - sym.modTree.get.definedSymbols.map: + case S(sym: BlockMemberSymbol) if sym.modOrObjTree.isDefined => + sym.modOrObjTree.get.definedSymbols.map: case (nme, sym) => nme -> Ctx.SelElem(baseElem, sym.nme, S(sym)) case _ => raise(ErrorReport(msg"Wildcard 'open' not supported for this kind of symbol." -> baseId.toLoc :: Nil)) @@ -697,6 +699,9 @@ extends Importer: val sym = fieldOrVarSym(LetBind, id) log(s"Processing `let` statement $id (${sym}) ${ctx.outer}") + members.get(id.name).foreach: s => + raise(ErrorReport(msg"Name '${id.name}' is already used" + -> hd.toLoc :: msg"by a member declared in the same block" -> s.toLoc :: Nil)) val newAcc = rhso match case S(rhs) => val rrhs = tups.foldRight(rhs): diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index d91404bb4..b82182e74 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -32,10 +32,12 @@ abstract class Symbol(using State) extends Located: case cls: ClassSymbol => S(cls) case mem: BlockMemberSymbol => mem.clsTree.flatMap(_.symbol.asCls) case _ => N - def asMod: Opt[ModuleSymbol] = this match + def asModOrObj: Opt[ModuleSymbol] = this match case mod: ModuleSymbol => S(mod) - case mem: BlockMemberSymbol => mem.modTree.flatMap(_.symbol.asMod) + case mem: BlockMemberSymbol => mem.modOrObjTree.flatMap(_.symbol.asModOrObj) case _ => N + def asMod: Opt[ModuleSymbol] = asModOrObj.filter(_.tree.k is Mod) + def asObj: Opt[ModuleSymbol] = asModOrObj.filter(_.tree.k is Obj) /* def asTrm: Opt[TermSymbol] = this match case trm: TermSymbol => S(trm) @@ -52,7 +54,7 @@ abstract class Symbol(using State) extends Located: case _ => N def asClsLike: Opt[ClassSymbol | ModuleSymbol | PatternSymbol] = - (asCls: Opt[ClassSymbol | ModuleSymbol | PatternSymbol]) orElse asMod orElse asPat + (asCls: Opt[ClassSymbol | ModuleSymbol | PatternSymbol]) orElse asModOrObj orElse asPat def asTpe: Opt[TypeSymbol] = asCls orElse asAls override def equals(x: Any): Bool = x match @@ -130,8 +132,11 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree])(using State) def clsTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if t.k is Cls => t + def modOrObjTree: Opt[Tree.TypeDef] = modTree orElse objTree + def objTree: Opt[Tree.TypeDef] = trees.collectFirst: + case t: Tree.TypeDef if (t.k is Obj) => t def modTree: Opt[Tree.TypeDef] = trees.collectFirst: - case t: Tree.TypeDef if (t.k is Mod) || (t.k is Obj) => t + case t: Tree.TypeDef if (t.k is Mod) => t def alsTree: Opt[Tree.TypeDef] = trees.collectFirst: case t: Tree.TypeDef if t.k is Als => t def patTree: Opt[Tree.TypeDef] = trees.collectFirst: @@ -142,7 +147,7 @@ class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree])(using State) case t: Tree.TermDef if t.rhs.isDefined => t lazy val hasLiftedClass: Bool = - modTree.isDefined || trmTree.isDefined || clsTree.exists(_.paramLists.nonEmpty) + objTree.isDefined || trmTree.isDefined || clsTree.exists(_.paramLists.nonEmpty) override def toString: Str = s"member:$nme${State.dbgUid(uid)}" @@ -201,6 +206,7 @@ sealed trait ClassLikeSymbol extends Symbol: // TODO prevent from appearing in Ref sealed trait InnerSymbol(using State) extends Symbol: val privatesScope: Scope = Scope.empty // * Scope for private members of this symbol + val thisProxy: TempSymbol = TempSymbol(N, s"this$$$nme") def subst(using SymbolSubst): InnerSymbol class ClassSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) @@ -219,7 +225,9 @@ class ModuleSymbol(val tree: Tree.TypeDef, val id: Tree.Ident)(using State) def name: Str = nme def nme = id.name def toLoc: Option[Loc] = id.toLoc // TODO track source tree of module here - override def toString: Str = s"module:${id.name}${State.dbgUid(uid)}" + override def toString: Str = + if tree.k is Obj then s"object:$nme${State.dbgUid(uid)}" + else s"module:${id.name}${State.dbgUid(uid)}" override def subst(using sub: SymbolSubst): ModuleSymbol = sub.mapModuleSym(this) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 15d4e8bed..d95cf6b85 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -1,9 +1,12 @@ package hkmc2 package semantics +import scala.collection.mutable.Buffer + import mlscript.utils.*, shorthands.* import syntax.* -import scala.collection.mutable.Buffer + +import Elaborator.State final case class QuantVar(sym: VarSymbol, ub: Opt[Term], lb: Opt[Term]) @@ -34,6 +37,7 @@ enum Term extends Statement: case TyApp(lhs: Term, targs: Ls[Term]) case Sel(prefix: Term, nme: Tree.Ident)(val sym: Opt[FieldSymbol]) case SynthSel(prefix: Term, nme: Tree.Ident)(val sym: Opt[FieldSymbol]) + case DynSel(prefix: Term, fld: Term, arrayIdx: Bool) case Tup(fields: Ls[Elem])(val tree: Tree.Tup) case IfLike(kw: Keyword.`if`.type | Keyword.`while`.type, desugared: Split)(val normalized: Split) case Lam(params: ParamList, body: Term) @@ -65,6 +69,15 @@ enum Term extends Statement: case sel: SelProj => sel.sym case _ => N + def sel(id: Tree.Ident, sym: Opt[FieldSymbol]) = + Sel(this, id)(sym) + def selNoSym(nme: Str) = + sel(Tree.Ident(nme), N) + + def app(args: Term*)(using State) = + App(this, Tup(args.toList.map(PlainFld(_)))(Tree.DummyTup))(Tree.App(Tree.Dummy, Tree.Dummy), + FlowSymbol("")) + def describe: Str = this match case Error => "" case Lit(lit) => lit.describeLit @@ -98,6 +111,10 @@ end Term import Term.* +extension (self: Blk) + def mapRes(f: Term => Term) = + Blk(self.stats, f(self.res)) + sealed trait Statement extends AutoLocated with ProductWithExtraInfo: def extraInfo: Str = this match @@ -112,8 +129,9 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo: case App(lhs, rhs) => lhs :: rhs :: Nil case FunTy(lhs, rhs, eff) => lhs :: rhs :: eff.toList case TyApp(pre, tarsg) => pre :: tarsg - case SynthSel(pre, _) => pre :: Nil case Sel(pre, _) => pre :: Nil + case SynthSel(pre, _) => pre :: Nil + case DynSel(o, f, _) => o :: f :: Nil case Tup(fields) => fields.flatMap(_.subTerms) case IfLike(_, body) => body.subTerms case Lam(params, body) => body :: Nil @@ -224,6 +242,8 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo: case Import(sym, file) => s"import ${sym} from ${file}" case Annotated(ann, target) => s"@${ann} ${target.showDbg}" case Throw(res) => s"throw ${res.showDbg}" + case Try(body, finallyDo) => s"try ${body.showDbg} finally ${finallyDo.showDbg}" + case Ret(res) => s"return ${res.showDbg}" final case class LetDecl(sym: LocalSymbol, annotations: Ls[Annot]) extends Statement diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala index 7b32d4242..89ccb7ebd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala @@ -744,7 +744,12 @@ abstract class Parser( val res = acc match case _ => InfixApp(PlainTup(acc), kw, rhs) exprCont(res, prec, allowNewlines) - case (IDENT(".", _), l0) :: (br @ BRACKETS(Round, toks), l1) :: _ => + case (IDENT(".", _), l0) :: (br @ BRACKETS(bk @ (Round | Square), toks), l1) :: _ => + consume + consume + val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0)) + exprCont(DynAccess(acc, inner, bk is Square), prec, allowNewlines) + case (IDENT(".", _), l0) :: (br @ BRACKETS(Curly, toks), l1) :: _ => consume consume val inner = rec(toks, S(br.innerLoc), br.describe).concludeWith(_.expr(0)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala index f08c3990c..f5148ebf0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala @@ -41,6 +41,7 @@ sealed trait Literal extends AutoLocated: enum Tree extends AutoLocated: case Empty() case Error() + case Dummy // TODO change the places where this is used case Under() case Ident(name: Str) case Keywrd(kw: Keyword) @@ -59,6 +60,7 @@ enum Tree extends AutoLocated: case TypeDef(k: TypeDefKind, head: Tree, extension: Opt[Tree], body: Opt[Tree])(using State) extends Tree with TypeDefImpl case Open(opened: Tree) case OpenIn(opened: Tree, body: Tree) + case DynAccess(obj: Tree, fld: Tree, arrayIdx: Bool) case Modified(modifier: Keyword, modLoc: Opt[Loc], body: Tree) case Quoted(body: Tree) case Unquoted(body: Tree) @@ -111,8 +113,9 @@ enum Tree extends AutoLocated: case Effectful(eff, body) => eff :: body :: Nil case Outer(name) => name.toList case TyTup(tys) => tys - case SynthSel(prefix, name) => prefix :: Nil case Sel(prefix, name) => prefix :: Nil + case SynthSel(prefix, name) => prefix :: Nil + case DynAccess(prefix, fld, ai) => prefix :: fld :: Nil case Open(bod) => bod :: Nil case Def(lhs, rhs) => lhs :: rhs :: Nil case Spread(_, _, body) => body.toList @@ -143,8 +146,10 @@ enum Tree extends AutoLocated: case TyTup(tys) => "type tuple" case App(lhs, rhs) => "application" case Jux(lhs, rhs) => "juxtaposition" - case SynthSel(prefix, name) => "synthetic selection" case Sel(prefix, name) => "selection" + case SynthSel(prefix, name) => "synthetic selection" + case DynAccess(prefix, name, true) => "dynamic index access" + case DynAccess(prefix, name, false) => "dynamic field access" case InfixApp(lhs, kw, rhs) => "infix operation" case New(body) => "new" case IfLike(Keyword.`if`, _, split) => "if expression" @@ -201,6 +206,8 @@ enum Tree extends AutoLocated: case _ => false object Tree: + val DummyApp: App = App(Dummy, Dummy) + val DummyTup: Tup = Tup(Dummy :: Nil) object Block: def mk(stmts: Ls[Tree])(using State): Tree = stmts match case Nil => UnitLit(true) diff --git a/hkmc2/shared/src/main/scala/hkmc2/utils/Scope.scala b/hkmc2/shared/src/main/scala/hkmc2/utils/Scope.scala index 16aca26c0..e83db2e1d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/utils/Scope.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/utils/Scope.scala @@ -17,7 +17,7 @@ import hkmc2.codegen.Local import hkmc2.codegen.js.JSBuilder -/** When `curThis`, it means this scope does not rebind `this`. +/** When `curThis` is N, it means this scope does not rebind `this`. * When `curThis` is Some(None), it means the scope rebinds `this` * to something unknown, following JavaScript's inane `this` handling in `function`s. * When `curThis` is Some(Some(sym)), it means the scope rebinds `this` @@ -33,7 +33,7 @@ class Scope case S(S(State.globalThisSymbol)) => "globalThis" case S(S(thisSym)) => thisProxyAccessed = true - allocateName(thisSym, "this$") + allocateName(thisSym.thisProxy) /** Whether the code generator has produced a binding for `thisProxy` yet. */ var thisProxyDefined: Bool = false @@ -126,6 +126,7 @@ object Scope: case c if c.isLetter || c.isDigit => c // case '\'' => "$tick" case '$' => "$" + case '_' => "_" case _ => "$_" .mkString diff --git a/hkmc2/shared/src/test/mlscript-compile/Example.mjs b/hkmc2/shared/src/test/mlscript-compile/Example.mjs index 9a837bd03..0c2de1f09 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Example.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Example.mjs @@ -1,14 +1,14 @@ import Predef from "./Predef.mjs"; let Example1; -const Example$class = class Example { - constructor() {} - funnySlash(f, arg) { +Example1 = class Example { + static {} + static funnySlash(f, arg) { return f(arg) ?? null; } - inc(x) { + static inc(x) { return x + 1; } - test(x1) { + static test(x1) { if (globalThis.Number.isInteger(x1)) { return "int"; } else { @@ -23,8 +23,7 @@ const Example$class = class Example { } } } - toString() { return "Example"; } -}; Example1 = new Example$class; -Example1.class = Example$class; + static toString() { return "Example"; } +}; null let Example = Example1; export default Example; diff --git a/hkmc2/shared/src/test/mlscript-compile/Option.mjs b/hkmc2/shared/src/test/mlscript-compile/Option.mjs index e7c6929b9..a22d93fb7 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Option.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Option.mjs @@ -1,13 +1,13 @@ import Predef from "./Predef.mjs"; let Option1; -const Option$class = class Option { - constructor() { +Option1 = class Option { + static { this.Some = function Some(value1) { return new Some.class(value1); }; this.Some.class = class Some { constructor(value) { this.value = value; } - toString() { return "Some(" + this.value + ")"; } + toString() { return "Some(" + globalThis.Predef.render(this.value) + ")"; } }; const None$class = class None { constructor() {} @@ -21,25 +21,24 @@ const Option$class = class Option { this.fst = fst; this.snd = snd; } - toString() { return "Both(" + this.fst + ", " + this.snd + ")"; } + toString() { return "Both(" + globalThis.Predef.render(this.fst) + ", " + globalThis.Predef.render(this.snd) + ")"; } }; } - isDefined(x) { - if (x instanceof this.Some.class) { + static isDefined(x) { + if (x instanceof Option.Some.class) { return true; } else { - if (x instanceof this.None.class) { + if (x instanceof Option.None.class) { return false; } else { throw new globalThis.Error("match error"); } } } - test() { + static test() { return Predef.pipeInto(2134, Predef.print); } - toString() { return "Option"; } -}; Option1 = new Option$class; -Option1.class = Option$class; + static toString() { return "Option"; } +}; null let Option = Option1; export default Option; diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index 32fcc7972..c70d679b4 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -1,55 +1,55 @@ let Predef1; -const Predef$class = class Predef { - constructor() { +Predef1 = class Predef { + static { this.assert = globalThis.console.assert; - this.foldl = this.fold; + this.foldl = Predef.fold; this.MatchResult = function MatchResult(captures1) { return new MatchResult.class(captures1); }; this.MatchResult.class = class MatchResult { constructor(captures) { this.captures = captures; } - toString() { return "MatchResult(" + this.captures + ")"; } + toString() { return "MatchResult(" + globalThis.Predef.render(this.captures) + ")"; } }; this.MatchFailure = function MatchFailure(errors1) { return new MatchFailure.class(errors1); }; this.MatchFailure.class = class MatchFailure { constructor(errors) { this.errors = errors; } - toString() { return "MatchFailure(" + this.errors + ")"; } + toString() { return "MatchFailure(" + globalThis.Predef.render(this.errors) + ")"; } }; - const TraceLogger$class = class TraceLogger { - constructor() { + this.TraceLogger = class TraceLogger { + static { this.enabled = false; this.indentLvl = 0; } - indent() { + static indent() { let scrut, prev, tmp; - scrut = this.enabled; + scrut = TraceLogger.enabled; if (scrut === true) { - prev = this.indentLvl; + prev = TraceLogger.indentLvl; tmp = prev + 1; - this.indentLvl = tmp; + TraceLogger.indentLvl = tmp; return prev; } else { return null; } } - resetIndent(n) { + static resetIndent(n) { let scrut; - scrut = this.enabled; + scrut = TraceLogger.enabled; if (scrut === true) { - this.indentLvl = n; + TraceLogger.indentLvl = n; return null; } else { return null; } } - log(msg) { + static log(msg) { let scrut, tmp, tmp1, tmp2, tmp3, tmp4; - scrut = this.enabled; + scrut = TraceLogger.enabled; if (scrut === true) { - tmp = "| ".repeat(this.indentLvl) ?? null; - tmp1 = " ".repeat(this.indentLvl) ?? null; + tmp = "| ".repeat(TraceLogger.indentLvl) ?? null; + tmp1 = " ".repeat(TraceLogger.indentLvl) ?? null; tmp2 = "\n" + tmp1; tmp3 = msg.replaceAll("\n", tmp2); tmp4 = tmp + tmp3; @@ -58,15 +58,12 @@ const Predef$class = class Predef { return null; } } - toString() { return "TraceLogger"; } + static toString() { return "TraceLogger"; } }; - this.TraceLogger = new TraceLogger$class; - this.TraceLogger.class = TraceLogger$class; - const this$Predef = this; this.Test = class Test { constructor() { let tmp; - tmp = this$Predef.print("Test"); + tmp = Predef.print("Test"); this.y = 1; } toString() { return "Test"; } @@ -76,14 +73,14 @@ const Predef$class = class Predef { constructor(next) { this.next = next; } - toString() { return "__Cont(" + this.next + ")"; } + toString() { return "__Cont(" + globalThis.Predef.render(this.next) + ")"; } }; this.__TailList = function __TailList(next1) { return new __TailList.class(next1); }; this.__TailList.class = class __TailList { constructor(next) { this.next = next; } - toString() { return "__TailList(" + this.next + ")"; } + toString() { return "__TailList(" + globalThis.Predef.render(this.next) + ")"; } }; this.__ListWithTail = function __ListWithTail(next1, tail1) { return new __ListWithTail.class(next1, tail1); }; this.__ListWithTail.class = class __ListWithTail { @@ -96,7 +93,7 @@ const Predef$class = class Predef { this.tail = elem; return null; } - toString() { return "__ListWithTail(" + this.next + ", " + this.tail + ")"; } + toString() { return "__ListWithTail(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.tail) + ")"; } }; this.__HandleBlock = function __HandleBlock(contHead1, lastHandlerCont1, next1, handler1) { return new __HandleBlock.class(contHead1, lastHandlerCont1, next1, handler1); }; this.__HandleBlock.class = class __HandleBlock { @@ -106,7 +103,7 @@ const Predef$class = class Predef { this.next = next; this.handler = handler; } - toString() { return "__HandleBlock(" + this.contHead + ", " + this.lastHandlerCont + ", " + this.next + ", " + this.handler + ")"; } + toString() { return "__HandleBlock(" + globalThis.Predef.render(this.contHead) + ", " + globalThis.Predef.render(this.lastHandlerCont) + ", " + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.handler) + ")"; } }; this.__EffectSig = function __EffectSig(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1) { return new __EffectSig.class(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1); }; this.__EffectSig.class = class __EffectSig { @@ -118,14 +115,14 @@ const Predef$class = class Predef { this.handler = handler; this.handlerFun = handlerFun; } - toString() { return "__EffectSig(" + this.next + ", " + this.tail + ", " + this.handleBlockList + ", " + this.resumed + ", " + this.handler + ", " + this.handlerFun + ")"; } + toString() { return "__EffectSig(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.tail) + ", " + globalThis.Predef.render(this.handleBlockList) + ", " + globalThis.Predef.render(this.resumed) + ", " + globalThis.Predef.render(this.handler) + ", " + globalThis.Predef.render(this.handlerFun) + ")"; } }; this.__Return = function __Return(value1) { return new __Return.class(value1); }; this.__Return.class = class __Return { constructor(value) { this.value = value; } - toString() { return "__Return(" + this.value + ")"; } + toString() { return "__Return(" + globalThis.Predef.render(this.value) + ")"; } }; this.__stackLimit = 0; this.__stackDepth = 0; @@ -134,89 +131,163 @@ const Predef$class = class Predef { this.__StackDelay = function __StackDelay() { return new __StackDelay.class(); }; this.__StackDelay.class = class __StackDelay { constructor() {} - toString() { return "__StackDelay(" + + ")"; } + toString() { return "__StackDelay(" + "" + ")"; } }; } - id(x) { + static id(x) { return x; } - not(x1) { + static not(x1) { if (x1 === false) { return true; } else { return false; } } - pipeInto(x2, f) { + static pipeInto(x2, f) { return f(x2) ?? null; } - pipeFrom(f1, x3) { + static pipeFrom(f1, x3) { return f1(x3) ?? null; } - andThen(f2, g) { + static andThen(f2, g) { return (x4) => { let tmp; tmp = f2(x4) ?? null; return g(tmp) ?? null; }; } - compose(f3, g1) { + static compose(f3, g1) { return (x4) => { let tmp; tmp = g1(x4) ?? null; return f3(tmp) ?? null; }; } - passTo(receiver, f4) { + static passTo(receiver, f4) { return (...args) => { return f4(receiver, ...args) ?? null; }; } - call(receiver1, f5) { + static call(receiver1, f5) { return (...args) => { return f5.call(receiver1, ...args); }; } - pass1(f6) { + static pass1(f6) { return (...xs) => { return f6(xs[0]) ?? null; }; } - pass2(f7) { + static pass2(f7) { return (...xs) => { return f7(xs[0], xs[1]) ?? null; }; } - pass3(f8) { + static pass3(f8) { return (...xs) => { return f8(xs[0], xs[1], xs[2]) ?? null; }; } - print(...xs) { - let tmp; - tmp = xs.map(globalThis.String) ?? null; - return globalThis.console.log(...tmp) ?? null; + static print(...xs) { + let tmp, tmp1; + tmp = Predef.map(Predef.renderAsStr); + tmp1 = tmp(...xs) ?? null; + return globalThis.console.log(...tmp1) ?? null; + } + static interleave(sep) { + return (...args) => { + let res, len, i, scrut, idx, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + scrut2 = args.length === 0; + if (scrut2 === true) { + return []; + } else { + tmp = args.length * 2; + tmp1 = tmp - 1; + tmp2 = globalThis.Array(tmp1); + res = tmp2; + len = args.length; + i = 0; + tmp8: while (true) { + scrut = i < len; + if (scrut === true) { + tmp3 = i * 2; + idx = tmp3; + res[idx] = args[i]; + tmp4 = i + 1; + i = tmp4; + scrut1 = i < len; + if (scrut1 === true) { + tmp5 = idx + 1; + res[tmp5] = sep; + tmp6 = null; + } else { + tmp6 = null; + } + tmp7 = tmp6; + continue tmp8; + } else { + tmp7 = null; + } + break; + } + return res; + } + }; + } + static renderAsStr(arg) { + if (typeof arg === 'string') { + return arg; + } else { + return Predef.render(arg); + } + } + static render(arg1) { + let tmp, tmp1, tmp2, tmp3, tmp4; + if (arg1 instanceof globalThis.Array) { + tmp = Predef.fold((arg11, arg2) => { + return arg11 + arg2; + }); + tmp1 = Predef.interleave(", "); + tmp2 = Predef.map(Predef.render); + tmp3 = tmp2(...arg1) ?? null; + tmp4 = tmp1(...tmp3) ?? null; + return tmp("[", ...tmp4, "]") ?? null; + } else { + if (typeof arg1 === 'string') { + return globalThis.JSON.stringify(arg1) ?? null; + } else { + return globalThis.String(arg1); + } + } } - notImplemented(msg) { + static notImplemented(msg) { let tmp; tmp = "Not implemented: " + msg; throw globalThis.Error(tmp); } - get notImplementedError() { + static get notImplementedError() { throw globalThis.Error("Not implemented"); } - tuple(...xs1) { + static tuple(...xs1) { return xs1; } - tupleSlice(xs2, i, j) { + static tupleSlice(xs2, i, j) { let tmp; tmp = xs2.length - j; return globalThis.Array.prototype.slice.call(xs2, i, tmp) ?? null; } - tupleGet(xs3, i1) { + static tupleGet(xs3, i1) { return globalThis.Array.prototype.at.call(xs3, i1); } - fold(f9) { + static map(f9) { + return (...xs4) => { + let tmp; + tmp = Predef.pass1(f9); + return xs4.map(tmp) ?? null; + }; + } + static fold(f10) { return (init, ...rest) => { let i2, len, scrut, tmp, tmp1, tmp2, tmp3; i2 = 0; @@ -225,7 +296,7 @@ const Predef$class = class Predef { scrut = i2 < len; if (scrut === true) { tmp = rest.at(i2) ?? null; - tmp1 = f9(init, tmp) ?? null; + tmp1 = f10(init, tmp) ?? null; init = tmp1; tmp2 = i2 + 1; i2 = tmp2; @@ -239,7 +310,7 @@ const Predef$class = class Predef { return init; }; } - foldr(f10) { + static foldr(f11) { return (first, ...rest) => { let len, i2, init, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; len = rest.length; @@ -257,7 +328,7 @@ const Predef$class = class Predef { tmp2 = i2 - 1; i2 = tmp2; tmp3 = rest.at(i2) ?? null; - tmp4 = f10(tmp3, init) ?? null; + tmp4 = f11(tmp3, init) ?? null; init = tmp4; tmp5 = null; continue tmp6; @@ -266,20 +337,20 @@ const Predef$class = class Predef { } break; } - return f10(first, init) ?? null; + return f11(first, init) ?? null; } }; } - stringStartsWith(string, prefix) { + static stringStartsWith(string, prefix) { return string.startsWith(prefix) ?? null; } - stringGet(string1, i2) { + static stringGet(string1, i2) { return string1.at(i2) ?? null; } - stringDrop(string2, n) { + static stringDrop(string2, n) { return string2.slice(n) ?? null; } - checkArgs(functionName, expected, isUB, got) { + static checkArgs(functionName, expected, isUB, got) { let scrut, name, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; tmp = got < expected; tmp1 = got > expected; @@ -294,8 +365,8 @@ const Predef$class = class Predef { tmp4 = ""; } name = tmp4; - tmp5 = this.fold((arg1, arg2) => { - return arg1 + arg2; + tmp5 = Predef.fold((arg11, arg2) => { + return arg11 + arg2; }); if (isUB === true) { tmp6 = ""; @@ -314,17 +385,17 @@ const Predef$class = class Predef { return null; } } - __mkListWithTail() { + static __mkListWithTail() { let res, tmp; - tmp = new this.__ListWithTail.class(null, null); + tmp = new Predef.__ListWithTail.class(null, null); res = tmp; res.tail = res; return res; } - __appendInCont(eff, cont) { + static __appendInCont(eff, cont) { let scrut, scrut1, tmp, tmp1; scrut = eff.tail; - if (scrut instanceof this.__TailList.class) { + if (scrut instanceof Predef.__TailList.class) { scrut1 = cont.next !== null; if (scrut1 === true) { throw globalThis.Error("unexpected handler continuation"); @@ -340,23 +411,23 @@ const Predef$class = class Predef { } return eff; } - __mkEffect(handler, handlerFun) { + static __mkEffect(handler, handlerFun) { let res, tmp, tmp1; - tmp = this.__mkListWithTail(); - tmp1 = new this.__EffectSig.class(null, null, tmp, false, handler, handlerFun); + tmp = Predef.__mkListWithTail(); + tmp1 = new Predef.__EffectSig.class(null, null, tmp, false, handler, handlerFun); res = tmp1; res.tail = res; return res; } - __handleBlockImpl(cur, handler1) { + static __handleBlockImpl(cur, handler1) { let handleBlock, nxt, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; - tmp = this.__TailList(null); - tmp1 = new this.__HandleBlock.class(tmp, null, null, handler1); + tmp = Predef.__TailList(null); + tmp1 = new Predef.__HandleBlock.class(tmp, null, null, handler1); handleBlock = tmp1; tmp2 = cur.handleBlockList.append(handleBlock) ?? null; tmp7: while (true) { - if (cur instanceof this.__EffectSig.class) { - tmp3 = this.__handleEffect(cur); + if (cur instanceof Predef.__EffectSig.class) { + tmp3 = Predef.__handleEffect(cur); nxt = tmp3; scrut = cur === nxt; if (scrut === true) { @@ -382,12 +453,12 @@ const Predef$class = class Predef { } return tmp6; } - __handleEffect(cur1) { + static __handleEffect(cur1) { let prevBlock, scrut, scrut1, scrut2, handleBlock, origTailBlock, savedNext, scrut3, scrut4, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; prevBlock = cur1.handleBlockList; tmp6: while (true) { scrut = prevBlock.next; - if (scrut instanceof this.__HandleBlock.class) { + if (scrut instanceof Predef.__HandleBlock.class) { scrut1 = prevBlock.next.handler !== cur1.handler; if (scrut1 === true) { prevBlock = prevBlock.next; @@ -412,7 +483,7 @@ const Predef$class = class Predef { prevBlock.next = null; cur1.handleBlockList.tail = prevBlock; savedNext = handleBlock.contHead.next; - tmp2 = this.__resume(cur1, handleBlock.contHead); + tmp2 = Predef.__resume(cur1, handleBlock.contHead); tmp3 = cur1.handlerFun(tmp2) ?? null; cur1 = tmp3; scrut3 = savedNext !== handleBlock.contHead.next; @@ -429,15 +500,15 @@ const Predef$class = class Predef { } else { tmp5 = null; } - if (cur1 instanceof this.__EffectSig.class) { + if (cur1 instanceof Predef.__EffectSig.class) { cur1.handleBlockList.tail.next = handleBlock; cur1.handleBlockList.tail = origTailBlock; return cur1; } else { - return this.__resumeHandleBlocks(handleBlock, origTailBlock, cur1); + return Predef.__resumeHandleBlocks(handleBlock, origTailBlock, cur1); } } - __resume(cur2, tail) { + static __resume(cur2, tail) { return (value) => { let scrut, cont1, scrut1, scrut2, scrut3, scrut4, scrut5, scrut6, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; scrut = cur2.resumed; @@ -449,10 +520,10 @@ const Predef$class = class Predef { cur2.resumed = true; cont1 = cur2.next; tmp10: while (true) { - if (cont1 instanceof this.__Cont.class) { + if (cont1 instanceof Predef.__Cont.class) { tmp1 = cont1.resume(value) ?? null; value = tmp1; - if (value instanceof this.__EffectSig.class) { + if (value instanceof Predef.__EffectSig.class) { scrut1 = value.tail.next !== cont1; if (scrut1 === true) { scrut2 = cont1.next !== null; @@ -502,9 +573,9 @@ const Predef$class = class Predef { if (scrut6 === true) { return value; } else { - tmp8 = this.__resumeHandleBlocks(cur2.handleBlockList.next, cur2.handleBlockList.tail, value); + tmp8 = Predef.__resumeHandleBlocks(cur2.handleBlockList.next, cur2.handleBlockList.tail, value); cur2 = tmp8; - if (cur2 instanceof this.__EffectSig.class) { + if (cur2 instanceof Predef.__EffectSig.class) { cur2.tail = tail; tmp9 = null; } else { @@ -514,14 +585,14 @@ const Predef$class = class Predef { } }; } - __resumeHandleBlocks(handleBlock, tailHandleBlock, value) { + static __resumeHandleBlocks(handleBlock, tailHandleBlock, value) { let scrut, scrut1, scrut2, scrut3, scrut4, tmp, tmp1, tmp2, tmp3, tmp4; tmp5: while (true) { scrut1 = handleBlock.contHead.next; - if (scrut1 instanceof this.__Cont.class) { + if (scrut1 instanceof Predef.__Cont.class) { tmp = handleBlock.contHead.next.resume(value) ?? null; value = tmp; - if (value instanceof this.__EffectSig.class) { + if (value instanceof Predef.__EffectSig.class) { scrut2 = value.tail.next !== handleBlock.contHead.next; if (scrut2 === true) { scrut3 = value.tail.next !== null; @@ -552,7 +623,7 @@ const Predef$class = class Predef { continue tmp5; } else { scrut = handleBlock.next; - if (scrut instanceof this.__HandleBlock.class) { + if (scrut instanceof Predef.__HandleBlock.class) { handleBlock = handleBlock.next; tmp4 = null; continue tmp5; @@ -564,8 +635,7 @@ const Predef$class = class Predef { } return tmp4; } - toString() { return "Predef"; } -}; Predef1 = new Predef$class; -Predef1.class = Predef$class; + static toString() { return "Predef"; } +}; null let Predef = Predef1; export default Predef; diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mls b/hkmc2/shared/src/test/mlscript-compile/Predef.mls index ee4406823..7c2d6b3cd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mls +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mls @@ -22,7 +22,29 @@ fun pass3(f)(...xs) = f(xs.0, xs.1, xs.2) fun print(...xs) = - console.log(...xs.map(String)) + console.log(...map(renderAsStr)(...xs)) + +fun interleave(sep)(...args) = + if args.length === 0 then [] else... + let + res = Array of args.length * 2 - 1 + len = args.length + i = 0 + while i < len do + let idx = i * 2 + set + res.[idx] = args.[i] + i += 1 + if i < len do set res.[idx + 1] = sep + res + +fun renderAsStr(arg) = + if arg is Str then arg else render(arg) + +fun render(arg) = if arg is + Array then fold(+)("[", ...interleave(", ")(...map(render)(...arg)), "]") + Str then JSON.stringify(arg) + else String(arg) val assert = console.assert @@ -38,6 +60,8 @@ fun tupleSlice(xs, i, j) = fun tupleGet(xs, i) = globalThis.Array.prototype.at.call(xs, i) +fun map(f)(...xs) = xs.map(pass1(f)) + fun fold(f)(init, ...rest) = let i = 0 diff --git a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs index 369f9ca60..b56ae448a 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs @@ -1,14 +1,14 @@ import Predef from "./Predef.mjs"; let Stack1; -const Stack$class = class Stack { - constructor() { +Stack1 = class Stack { + static { this.Cons = function Cons(head1, tail1) { return new Cons.class(head1, tail1); }; this.Cons.class = class Cons { constructor(head, tail) { this.head = head; this.tail = tail; } - toString() { return "Cons(" + this.head + ", " + this.tail + ")"; } + toString() { return "Cons(" + globalThis.Predef.render(this.head) + ", " + globalThis.Predef.render(this.tail) + ")"; } }; const Nil$class = class Nil { constructor() {} @@ -17,43 +17,43 @@ const Stack$class = class Stack { this.Nil = new Nil$class; this.Nil.class = Nil$class; } - isEmpty(xs) { - if (xs instanceof this.Nil.class) { + static isEmpty(xs) { + if (xs instanceof Stack.Nil.class) { return true; } else { return false; } } - reverseAndAppend(xs1, tail) { + static reverseAndAppend(xs1, tail) { let param0, param1, h, t, tmp; - if (xs1 instanceof this.Cons.class) { + if (xs1 instanceof Stack.Cons.class) { param0 = xs1.head; param1 = xs1.tail; h = param0; t = param1; - tmp = this.Cons(h, tail); - return this.reverseAndAppend(t, tmp); + tmp = Stack.Cons(h, tail); + return Stack.reverseAndAppend(t, tmp); } else { - if (xs1 instanceof this.Nil.class) { + if (xs1 instanceof Stack.Nil.class) { return tail; } else { throw new globalThis.Error("match error"); } } } - reverse(xs2) { - return this.reverseAndAppend(xs2, this.Nil); + static reverse(xs2) { + return Stack.reverseAndAppend(xs2, Stack.Nil); } - fromArray(arr) { + static fromArray(arr) { let ls, i, len, scrut, tmp, tmp1, tmp2, tmp3; - ls = this.Nil; + ls = Stack.Nil; i = 0; len = arr.length; tmp4: while (true) { scrut = i < len; if (scrut === true) { tmp = arr.at(i) ?? null; - tmp1 = this.Cons(tmp, ls); + tmp1 = Stack.Cons(tmp, ls); ls = tmp1; tmp2 = i + 1; i = tmp2; @@ -66,12 +66,12 @@ const Stack$class = class Stack { } return ls; } - toReverseArray(xs3) { + static toReverseArray(xs3) { let arr1, i, param0, param1, h, t, tmp, tmp1; arr1 = []; i = 0; tmp2: while (true) { - if (xs3 instanceof this.Cons.class) { + if (xs3 instanceof Stack.Cons.class) { param0 = xs3.head; param1 = xs3.tail; h = param0; @@ -87,28 +87,27 @@ const Stack$class = class Stack { } return arr1; } - zip(...xss) { + static zip(...xss) { let go, tmp, tmp1; - const this$Stack = this; go = function go(heads, tails) { return (caseScrut) => { let param0, param1, h, t, param01, param11, h2, t2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; - if (caseScrut instanceof this$Stack.Cons.class) { + if (caseScrut instanceof Stack.Cons.class) { param0 = caseScrut.head; param1 = caseScrut.tail; h = param0; t = param1; - if (h instanceof this$Stack.Cons.class) { + if (h instanceof Stack.Cons.class) { param01 = h.head; param11 = h.tail; h2 = param01; t2 = param11; - tmp2 = this$Stack.Cons(h2, heads); - tmp3 = this$Stack.Cons(t2, tails); + tmp2 = Stack.Cons(h2, heads); + tmp3 = Stack.Cons(t2, tails); tmp4 = go(tmp2, tmp3); return tmp4(t) ?? null; } else { - if (h instanceof this$Stack.Nil.class) { + if (h instanceof Stack.Nil.class) { tmp5 = go(heads, tails); return tmp5(t) ?? null; } else { @@ -116,21 +115,21 @@ const Stack$class = class Stack { } } } else { - if (caseScrut instanceof this$Stack.Nil.class) { - if (heads instanceof this$Stack.Nil.class) { - if (tails instanceof this$Stack.Nil.class) { + if (caseScrut instanceof Stack.Nil.class) { + if (heads instanceof Stack.Nil.class) { + if (tails instanceof Stack.Nil.class) { tmp6 = true; } else { tmp6 = false; } tmp7 = Predef.assert(tmp6) ?? null; - return (tmp7 , this$Stack.Nil); + return (tmp7 , Stack.Nil); } else { - tmp8 = this$Stack.toReverseArray(heads); - tmp9 = go(this$Stack.Nil, this$Stack.Nil); - tmp10 = this$Stack.reverse(tails); + tmp8 = Stack.toReverseArray(heads); + tmp9 = go(Stack.Nil, Stack.Nil); + tmp10 = Stack.reverse(tails); tmp11 = tmp9(tmp10) ?? null; - return this$Stack.Cons(tmp8, tmp11); + return Stack.Cons(tmp8, tmp11); } } else { throw new globalThis.Error("match error"); @@ -138,12 +137,11 @@ const Stack$class = class Stack { } }; }; - tmp = go(this.Nil, this.Nil); - tmp1 = this.fromArray(xss); + tmp = go(Stack.Nil, Stack.Nil); + tmp1 = Stack.fromArray(xss); return tmp(tmp1) ?? null; } - toString() { return "Stack"; } -}; Stack1 = new Stack$class; -Stack1.class = Stack$class; + static toString() { return "Stack"; } +}; null let Stack = Stack1; export default Stack; diff --git a/hkmc2/shared/src/test/mlscript-compile/Str.mjs b/hkmc2/shared/src/test/mlscript-compile/Str.mjs index c11e334c3..e0aef22eb 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Str.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Str.mjs @@ -1,17 +1,16 @@ let Str1; -const Str$class = class Str { - constructor() {} - concat2(a, b) { +Str1 = class Str { + static {} + static concat2(a, b) { return a + b; } - concat(...xs) { + static concat(...xs) { return xs.join("") ?? null; } - from(value) { + static from(value) { return globalThis.String(value) ?? null; } - toString() { return "Str"; } -}; Str1 = new Str$class; -Str1.class = Str$class; + static toString() { return "Str"; } +}; null let Str = Str1; export default Str; diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs index 0d152a04e..70930cd43 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs @@ -10,15 +10,15 @@ Accounting1 = class Accounting { constructor(num) { this.num = num; } - toString() { return "Project(" + this.num + ")"; } + toString() { return "Project(" + globalThis.Predef.render(this.num) + ")"; } }; const this$Accounting = this; - this.Line = function Line(name1, proj1, starting$_balance1, isMatchable1) { return new Line.class(name1, proj1, starting$_balance1, isMatchable1); }; + this.Line = function Line(name1, proj1, starting_balance1, isMatchable1) { return new Line.class(name1, proj1, starting_balance1, isMatchable1); }; this.Line.class = class Line { - constructor(name, proj, starting$_balance, isMatchable) { + constructor(name, proj, starting_balance, isMatchable) { this.name = name; this.proj = proj; - this.starting_balance = starting$_balance; + this.starting_balance = starting_balance; this.isMatchable = isMatchable; this.balance = this.starting_balance; } @@ -42,7 +42,7 @@ Accounting1 = class Accounting { return null; } } - toString() { return "Line(" + this.name + ", " + this.proj + ", " + this.starting_balance + ", " + this.isMatchable + ")"; } + toString() { return "Line(" + globalThis.Predef.render(this.name) + ", " + globalThis.Predef.render(this.proj) + ", " + globalThis.Predef.render(this.starting_balance) + ", " + globalThis.Predef.render(this.isMatchable) + ")"; } }; this.lines = []; this.Report = function Report(fileName1) { return new Report.class(fileName1); }; @@ -142,7 +142,7 @@ Accounting1 = class Accounting { tmp26 = Str.concat2(tmp25, "|"); return this.wln(tmp26); } - toString() { return "Report(" + this.fileName + ")"; } + toString() { return "Report(" + globalThis.Predef.render(this.fileName) + ")"; } }; } display(amt) { @@ -150,9 +150,9 @@ Accounting1 = class Accounting { tmp = amt / 1000; return tmp.toFixed(1) ?? null; } - mkLine(nme, proj, starting$_balance, matchable) { + mkLine(nme, proj, starting_balance, matchable) { let line, tmp, tmp1; - tmp = this.Line(nme, proj, starting$_balance, matchable); + tmp = this.Line(nme, proj, starting_balance, matchable); line = tmp; tmp1 = this.lines.push(line) ?? null; return line; diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs index 01f1a12d8..e07ba625d 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs @@ -53,7 +53,7 @@ CSV1.class = class CSV { } return arrData; } - toString() { return "CSV(" + this.strDelimiter + ")"; } + toString() { return "CSV(" + globalThis.Predef.render(this.strDelimiter) + ")"; } }; null let CSV = CSV1; export default CSV; diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index 92bac09fd..30110957f 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -120,14 +120,15 @@ Infinity :sjs val Infinity = 1 //│ JS (unsanitized): -//│ let Infinity1; Infinity1 = 1; null -//│ Infinity = 1 +//│ let Infinity; Infinity = 1; null +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Identifier 'Infinity' has already been declared +//│ Infinity = Infinity :sjs Infinity //│ JS (unsanitized): -//│ Infinity1 -//│ = 1 +//│ Infinity +//│ = Infinity module Test with val Infinity = 1 @@ -148,7 +149,7 @@ f of 2 of 3 //│ ╔══[PARSE ERROR] Expected end of input; found indented block instead -//│ ║ l.149: of 3 +//│ ║ l.150: of 3 //│ ╙── ^^ //│ = [Function (anonymous)] @@ -179,7 +180,7 @@ object Oops with :e Oops.fakeField //│ ╔══[ERROR] Object 'Oops' does not contain member 'fakeField' -//│ ║ l.180: Oops.fakeField +//│ ║ l.181: Oops.fakeField //│ ╙── ^^^^^^^^^^ //│ = 1 @@ -238,7 +239,7 @@ object Cls(x) with //│ get huh() { //│ return this.x; //│ } -//│ toString() { return "Cls(" + this.x + ")"; } +//│ toString() { return "Cls(" + globalThis.Predef.render(this.x) + ")"; } //│ }; Cls1 = new Cls$class; //│ Cls1.class = Cls$class; //│ null @@ -246,7 +247,7 @@ object Cls(x) with :e Cls.x //│ ╔══[ERROR] Object 'Cls' does not contain member 'x' -//│ ║ l.247: Cls.x +//│ ║ l.248: Cls.x //│ ╙── ^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' @@ -262,3 +263,28 @@ c.x // ——— ——— ——— +class Foo' with + class Bar' +//│ > let Foo$_1;try { Foo$_1 = class Foo$_ { constructor() { this.Bar' = class Bar$_ { constructor() {} toString() { return "Bar'"; } }; } toString() { return "Foo'"; } }; null } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Unexpected string + +// ——— ——— ——— + +module Example with +// whoops + val a = this +//│ ╔══[PARSE ERROR] Expected block after type declaration body; found newline instead +//│ ║ l.274: module Example with +//│ ║ ^ +//│ ║ l.275: // whoops +//│ ╙── +//│ ╔══[PARSE ERROR] Expected an expression; found block instead +//│ ║ l.276: val a = this +//│ ╙── ^ +//│ ╔══[PARSE ERROR] Expected end of input; found indented block instead +//│ ║ l.276: val a = this +//│ ╙── ^^ + +// ——— ——— ——— + diff --git a/hkmc2/shared/src/test/mlscript/backlog/UCS.mls b/hkmc2/shared/src/test/mlscript/backlog/UCS.mls index 503b18ad7..b0df77a27 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/UCS.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/UCS.mls @@ -83,7 +83,7 @@ let audits = new Set() :fixme if audits.has(1) === true do print("ok") -else +else 1 //│ ╔══[ERROR] The following branches are unreachable. //│ ╟── Because the previous split is full. diff --git a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls index f702b6d95..fe33e19c8 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls @@ -15,13 +15,13 @@ x :sjs val ++ = 0 //│ JS (unsanitized): -//│ let $_$_1; $_$_1 = 0; null +//│ let $_$_; $_$_ = 0; null //│ ++ = 0 :sjs ++ //│ JS (unsanitized): -//│ $_$_1 +//│ $_$_ //│ = 0 @@ -48,7 +48,7 @@ fun ++ z = 0 //│ ╔══[ERROR] Invalid function definition head: unexpected identifier in this position //│ ║ l.47: fun ++ z = 0 //│ ╙── ^ -//│ > let $_$_5;try { $_$_5 = function ++(...args) { globalThis.Predef.checkArgs("++", 0, true, args.length); return 0; }; null } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > let $_$_2;try { $_$_2 = function ++(...args) { globalThis.Predef.checkArgs("++", 0, true, args.length); return 0; }; null } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Unexpected token '++' @@ -56,8 +56,8 @@ fun ++ z = 0 :re ++ //│ JS (unsanitized): -//│ let tmp; tmp = $_$_5(); tmp -//│ ═══[RUNTIME ERROR] ReferenceError: $_$_5 is not defined +//│ let tmp; tmp = $_$_2(); tmp +//│ ═══[RUNTIME ERROR] ReferenceError: $_$_2 is not defined :e diff --git a/hkmc2/shared/src/test/mlscript/basics/BadModuleUses.mls b/hkmc2/shared/src/test/mlscript/basics/BadModuleUses.mls index cf39ecf29..fe49db1b9 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadModuleUses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadModuleUses.mls @@ -11,7 +11,7 @@ module Example with fun foo(): module M = M let m = Example.foo() -//│ m = M { class: [class M] } +//│ m = [class M] :todo // FIXME: should be an error :e @@ -20,14 +20,14 @@ id(m).mtd() Example.foo() -//│ = M { class: [class M] } +//│ = [class M] :e id(Example.foo()) //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.26: id(Example.foo()) //│ ╙── ^^^^^^^^^^^^^ -//│ = M { class: [class M] } +//│ = [class M] Example.foo().mtd() //│ = 42 @@ -45,14 +45,14 @@ M |> id //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.44: M |> id //│ ╙── ^ -//│ = M { class: [class M] } +//│ = [class M] :e id <| M //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.51: id <| M //│ ╙── ^ -//│ = M { class: [class M] } +//│ = [class M] fun (+) lol(a, b) = [a, b] @@ -61,14 +61,14 @@ M + 1 //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.60: M + 1 //│ ╙── ^ -//│ = [ M { class: [class M] }, 1 ] +//│ = [ [class M], 1 ] :todo // FIXME: should be an error :e let m = M -//│ m = M { class: [class M] } +//│ m = [class M] diff --git a/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls b/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls new file mode 100644 index 000000000..53730b2fe --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/basics/DynamicFields.mls @@ -0,0 +1,66 @@ +:js + + +let xs = [1, 2, 3] +//│ xs = [ 1, 2, 3 ] + +:sjs +xs.(0) +//│ JS (unsanitized): +//│ xs[0] +//│ = 1 + +:sjs +set xs.(0) = 4 +//│ JS (unsanitized): +//│ xs[0] = 4; null + +xs.(0) +//│ = 4 + +:todo +set xs.[0] += 1 +//│ /!!!\ Uncaught error: scala.MatchError: LetLike(keyword 'set',App(Ident(+=),Tup(List(DynAccess(Ident(xs),IntLit(0),true), IntLit(1)))),None,None) (of class hkmc2.syntax.Tree$LetLike) + +set xs.[0] = xs.[0] + 1 + +xs.[0] +//│ = 5 + +set xs.("lol") = "wat" + +xs +//│ = [ 5, 2, 3, lol: 'wat' ] + + +class Foo(val x) + +let foo = Foo(123) +//│ foo = Foo { x: 123 } + +foo.("x") +//│ = 123 + +// TODO sanitize +:todo +:re +foo.("y") + +// TODO sanitize +:todo +:re +foo.(0) + +// TODO sanitize +:todo +:re +foo.[0] + +// TODO sanitize +:todo +// * Array indexing syntax is only for numeric indices – this should throw +:re +foo.["x"] +//│ = 123 + + diff --git a/hkmc2/shared/src/test/mlscript/basics/Indentation.mls b/hkmc2/shared/src/test/mlscript/basics/Indentation.mls index 093edf50f..9aa47077c 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Indentation.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Indentation.mls @@ -87,6 +87,6 @@ fun checkArgs(expected) = module TraceLogger P.TraceLogger -//│ = TraceLogger { class: [class TraceLogger] } +//│ = [class TraceLogger1] diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index 7b85edc31..26ea98a7a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -56,10 +56,10 @@ foo m() //│ = 124 -M.(foo Foo::m()) +M.{foo Foo::m()} //│ = 124 -M.(foo.Foo#m()) +M.{foo.Foo#m()} //│ = 124 do diff --git a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls index a3b4f390a..e354bd106 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls @@ -5,13 +5,13 @@ let xs = [0, 1, 2] //│ xs = [ 0, 1, 2 ] xs. print() -//│ > 0,1,2 +//│ > [0, 1, 2] let f = xs. print //│ f = [Function (anonymous)] f() -//│ > 0,1,2 +//│ > [0, 1, 2] xs.reverse() //│ = [ 2, 1, 0 ] @@ -36,7 +36,7 @@ xs.map((x, ..._) => x * 2) fun map(xs, f) = xs.map((x, ...bs) => f(x)) xs. map(x => x * 2). print() -//│ > 4,2,0 +//│ > [4, 2, 0] :fixme :p diff --git a/hkmc2/shared/src/test/mlscript/basics/ModuleMethods.mls b/hkmc2/shared/src/test/mlscript/basics/ModuleMethods.mls index 982e6f88d..54466f036 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ModuleMethods.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ModuleMethods.mls @@ -52,14 +52,14 @@ f6(M) //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.51: f6(M) //│ ╙── ^ -//│ = M { class: [class M] } +//│ = [class M] :e f6(M.self()) //│ ╔══[ERROR] Only module parameters may receive module arguments (values). //│ ║ l.58: f6(M.self()) //│ ╙── ^^^^^^^^ -//│ = M { class: [class M] } +//│ = [class M] :e fun f7(): module M @@ -88,9 +88,9 @@ module N with fun ok2(): module M = M ok1(M) -//│ = M { class: [class M] } +//│ = [class M] ok1(M.self()) -//│ = M { class: [class M] } +//│ = [class M] diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls index 0f8ee2ea1..47a18d9f0 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls @@ -6,11 +6,11 @@ fun f(n1: Int): Int = n1 //│ JS (unsanitized): -//│ let f1; f1 = function f(n1) { return n1; }; null +//│ let f; f = function f(n1) { return n1; }; null f(42) //│ JS (unsanitized): -//│ f1(42) +//│ f(42) //│ = 42 // TODO compile this to @@ -19,20 +19,20 @@ f(42) fun f(n1: Int)(n2: Int): Int = (10 * n1 + n2) //│ JS (unsanitized): -//│ let f3; f3 = function f(n1) { return (n2) => { let tmp; tmp = 10 * n1; return tmp + n2; }; }; null +//│ let f1; f1 = function f(n1) { return (n2) => { let tmp; tmp = 10 * n1; return tmp + n2; }; }; null // TODO compile this to // this.f$(4, 2) f(4)(2) //│ JS (unsanitized): -//│ let tmp; tmp = f3(4); tmp(2) ?? null +//│ let tmp; tmp = f1(4); tmp(2) ?? null //│ = 42 fun f(n1: Int)(n2: Int)(n3: Int): Int = 10 * (10 * n1 + n2) + n3 //│ JS (unsanitized): -//│ let f5; -//│ f5 = function f(n1) { +//│ let f2; +//│ f2 = function f(n1) { //│ return (n2) => { //│ return (n3) => { //│ let tmp1, tmp2, tmp3; @@ -47,13 +47,13 @@ fun f(n1: Int)(n2: Int)(n3: Int): Int = 10 * (10 * n1 + n2) + n3 f(4)(2)(0) //│ JS (unsanitized): -//│ let tmp1, tmp2; tmp1 = f5(4); tmp2 = tmp1(2) ?? null; tmp2(0) ?? null +//│ let tmp1, tmp2; tmp1 = f2(4); tmp2 = tmp1(2) ?? null; tmp2(0) ?? null //│ = 420 fun f(n1: Int)(n2: Int)(n3: Int)(n4: Int): Int = 10 * (10 * (10 * n1 + n2) + n3) + n4 //│ JS (unsanitized): -//│ let f7; -//│ f7 = function f(n1) { +//│ let f3; +//│ f3 = function f(n1) { //│ return (n2) => { //│ return (n3) => { //│ return (n4) => { @@ -72,7 +72,7 @@ fun f(n1: Int)(n2: Int)(n3: Int)(n4: Int): Int = 10 * (10 * (10 * n1 + n2) + n3) f(3)(0)(3)(1) //│ JS (unsanitized): -//│ let tmp3, tmp4, tmp5; tmp3 = f7(3); tmp4 = tmp3(0) ?? null; tmp5 = tmp4(3) ?? null; tmp5(1) ?? null +//│ let tmp3, tmp4, tmp5; tmp3 = f3(3); tmp4 = tmp3(0) ?? null; tmp5 = tmp4(3) ?? null; tmp5(1) ?? null //│ = 3031 diff --git a/hkmc2/shared/src/test/mlscript/basics/MutVal.mls b/hkmc2/shared/src/test/mlscript/basics/MutVal.mls index 3c28b56ad..cc45e71b1 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MutVal.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MutVal.mls @@ -7,7 +7,7 @@ mut val cached: Int = 1 :sjs set cached = 2 //│ JS (unsanitized): -//│ cached1 = 2; null +//│ cached = 2; null cached //│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/basics/OpenIn.mls b/hkmc2/shared/src/test/mlscript/basics/OpenIn.mls index 81d0e4fbb..2fe0dcb2c 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpenIn.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpenIn.mls @@ -3,16 +3,16 @@ import "../../mlscript-compile/Str.mls" -Str.(~) +Str.{~} //│ = [Function: concat2] -Str.(~)("a", "b", "c") +Str.{~}("a", "b", "c") //│ = 'ab' -Str.("a" ~ "b") +Str.{"a" ~ "b"} //│ = 'ab' -Str.("a" ~ from(123)) +Str.{"a" ~ from(123)} //│ = 'a123' diff --git a/hkmc2/shared/src/test/mlscript/basics/Overloading.mls b/hkmc2/shared/src/test/mlscript/basics/Overloading.mls index e3bf1bb15..ff19164b2 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Overloading.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Overloading.mls @@ -16,10 +16,10 @@ module Foo with { val x = 1 } //│ ╙── ^^^^^^^^^^^^^^^^^^^^ Foo -//│ = Foo { x: 1, class: [class Foo] } +//│ = [class Foo] { x: 1 } :todo Foo() -//│ ═══[RUNTIME ERROR] TypeError: Foo2 is not a function +//│ ═══[RUNTIME ERROR] TypeError: Class constructor Foo cannot be invoked without 'new' diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index 3a4f42ab8..01f1cc9ac 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -68,7 +68,7 @@ class Foo(x: Int) //│ constructor(x1) { //│ this.x = x1; //│ } -//│ toString() { return "Foo(" + this.x + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ null //│ Type: ⊤ @@ -93,14 +93,14 @@ let foo = new Foo(42) in foo.Foo#x :sjs fun inc(x) = x + 1 //│ JS (unsanitized): -//│ let inc1; inc1 = function inc(x1) { return x1 + 1; }; null +//│ let inc; inc = function inc(x1) { return x1 + 1; }; null //│ Type: ⊤ :sjs inc(41) //│ JS (unsanitized): -//│ inc1(41) +//│ inc(41) //│ = 42 //│ Type: Int @@ -139,7 +139,7 @@ if foo is Foo then 1 else 0 //│ 0 //│ } //│ = 1 -//│ foo = Foo {} +//│ foo = Foo2 {} //│ Type: Int @@ -148,15 +148,15 @@ fun pow(x) = case 0 then 1 n then x * pow(x)(n-1) //│ JS (unsanitized): -//│ let pow1; -//│ pow1 = function pow(x1) { +//│ let pow; +//│ pow = function pow(x1) { //│ return (caseScrut) => { //│ let n, tmp2, tmp3, tmp4; //│ if (caseScrut === 0) { //│ return 1; //│ } else { //│ n = caseScrut; -//│ tmp2 = pow1(x1); +//│ tmp2 = pow(x1); //│ tmp3 = n - 1; //│ tmp4 = tmp2(tmp3) ?? null; //│ return x1 * tmp4; @@ -172,8 +172,8 @@ fun not = case true then false false then true //│ JS (unsanitized): -//│ let not1; -//│ not1 = function not() { +//│ let not; +//│ not = function not() { //│ return (caseScrut) => { //│ if (caseScrut === true) { //│ return false; @@ -194,7 +194,7 @@ fun not = case :sjs not of false //│ JS (unsanitized): -//│ let tmp2; tmp2 = not1(); tmp2(false) +//│ let tmp2; tmp2 = not(); tmp2(false) //│ = true //│ Type: Bool @@ -204,15 +204,15 @@ fun fact = case 0 then 1 n then n * fact(n - 1) //│ JS (unsanitized): -//│ let fact1; -//│ fact1 = function fact() { +//│ let fact; +//│ fact = function fact() { //│ return (caseScrut) => { //│ let n, tmp3, tmp4, tmp5; //│ if (caseScrut === 0) { //│ return 1; //│ } else { //│ n = caseScrut; -//│ tmp3 = fact1(); +//│ tmp3 = fact(); //│ tmp4 = n - 1; //│ tmp5 = tmp3(tmp4); //│ return n * tmp5; diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls index 684b963dd..fe3f1cd75 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls @@ -75,8 +75,8 @@ fun test2() = funny //│ ═══[WARNING] Pure expression in statement position //│ JS (unsanitized): -//│ let test25; -//│ test25 = function test2() { +//│ let test22; +//│ test22 = function test2() { //│ let funny, tmp1; //│ funny = function funny() { //│ return (caseScrut) => { diff --git a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls index 7c4f3c4f9..d6c661a88 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Arrays.mls @@ -24,7 +24,7 @@ single.0 :sjs val single = [1] //│ JS (unsanitized): -//│ let single2; single2 = [ 1 ]; null +//│ let single1; single1 = [ 1 ]; null //│ single = [ 1 ] single.0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls b/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls index bf09fb007..c1cfda417 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadInit.mls @@ -10,21 +10,18 @@ module Bar with val a = Bar.x //│ JS (unsanitized): //│ let Bar1; -//│ const Bar$class = class Bar { -//│ constructor() { +//│ Bar1 = class Bar { +//│ static { //│ this.x = 1; -//│ const Baz$class = class Baz { -//│ constructor() { +//│ this.Baz = class Baz { +//│ static { //│ this.a = Bar1.x; //│ } -//│ toString() { return "Baz"; } +//│ static toString() { return "Baz"; } //│ }; -//│ this.Baz = new Baz$class; -//│ this.Baz.class = Baz$class; //│ } -//│ toString() { return "Bar"; } -//│ }; Bar1 = new Bar$class; -//│ Bar1.class = Bar$class; +//│ static toString() { return "Bar"; } +//│ }; //│ null //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls index d7a212e26..3dec58527 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls @@ -83,7 +83,7 @@ fun (+) lol(a, b) = [a, b] :sjs 1 + 2 //│ JS (unsanitized): -//│ lol1(1, 2) +//│ lol(1, 2) //│ = [ 1, 2 ] diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls index 9ece4a90f..57d876332 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseOfCase.mls @@ -18,8 +18,8 @@ fun test(x) = Some(v) then log(v) None then log("none") //│ JS (unsanitized): -//│ let test1; -//│ test1 = function test(x) { +//│ let test; +//│ test = function test(x) { //│ let param0, v, scrut, param01, v1, tmp, tmp1; //│ if (x instanceof Some1.class) { //│ param0 = x.value; @@ -27,7 +27,7 @@ fun test(x) = //│ tmp = v + 1; //│ tmp1 = Some1(tmp); //│ } else { -//│ if (x instanceof None1.class) { +//│ if (x instanceof None1) { //│ tmp1 = None1; //│ } else { //│ throw new globalThis.Error("match error"); @@ -37,10 +37,10 @@ fun test(x) = //│ if (scrut instanceof Some1.class) { //│ param01 = scrut.value; //│ v1 = param01; -//│ return log1(v1); +//│ return log(v1); //│ } else { -//│ if (scrut instanceof None1.class) { -//│ return log1("none"); +//│ if (scrut instanceof None1) { +//│ return log("none"); //│ } else { //│ throw new globalThis.Error("match error"); //│ } diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index 3bff29053..f9287f396 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -64,19 +64,19 @@ val isDefined = case Some then true None then false //│ JS (unsanitized): -//│ let isDefined1, tmp5; +//│ let isDefined, tmp5; //│ tmp5 = (caseScrut) => { //│ if (caseScrut instanceof Some1.class) { //│ return true; //│ } else { -//│ if (caseScrut instanceof None1.class) { +//│ if (caseScrut instanceof None1) { //│ return false; //│ } else { //│ throw new this.Error("match error"); //│ } //│ } //│ }; -//│ isDefined1 = tmp5; +//│ isDefined = tmp5; //│ null //│ isDefined = [Function: tmp5] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls index ce0e06e9d..200adde68 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInClass.mls @@ -34,10 +34,10 @@ class Outer(a, b) with //│ constructor(c) { //│ this.c = c; //│ let tmp3, tmp4, tmp5; -//│ tmp3 = log1(this$Outer.a); -//│ tmp4 = log1(this.c); +//│ tmp3 = log(this$Outer.a); +//│ tmp4 = log(this.c); //│ tmp5 = this.i1(this$Outer.a); -//│ log1(tmp5) +//│ log(tmp5) //│ } //│ i1(d) { //│ return [ @@ -46,13 +46,13 @@ class Outer(a, b) with //│ d //│ ]; //│ } -//│ toString() { return "Inner(" + this.c + ")"; } +//│ toString() { return "Inner(" + globalThis.Predef.render(this.c) + ")"; } //│ }; //│ tmp = this.Inner(this.a); //│ this.i = tmp; -//│ tmp1 = log1(this.i.c); +//│ tmp1 = log(this.i.c); //│ tmp2 = this.i.i1(1) ?? null; -//│ log1(tmp2) +//│ log(tmp2) //│ } //│ o1(c) { //│ return this.Inner(c); @@ -62,7 +62,7 @@ class Outer(a, b) with //│ tmp = this.Inner(c1); //│ return tmp.i1(d) ?? null; //│ } -//│ toString() { return "Outer(" + this.a + ", " + this.b + ")"; } +//│ toString() { return "Outer(" + globalThis.Predef.render(this.a) + ", " + globalThis.Predef.render(this.b) + ")"; } //│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls index 08643bfd8..ad0ecdc6f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls @@ -9,16 +9,16 @@ fun test(a) = class C with { val x = a } new C //│ JS (unsanitized): -//│ let test1; -//│ test1 = function test(a) { -//│ let C; -//│ C = class C { +//│ let test; +//│ test = function test(a) { +//│ let C1; +//│ C1 = class C { //│ constructor() { //│ this.x = a; //│ } //│ toString() { return "C"; } //│ }; -//│ return new C(); +//│ return new C1(); //│ }; //│ null @@ -37,27 +37,27 @@ fun test(x) = class Foo(a, b) Foo(x, x + 1) //│ JS (unsanitized): -//│ let test5; -//│ test5 = function test(x) { -//│ let Foo, tmp; -//│ Foo = function Foo(a1, b1) { +//│ let test2; +//│ test2 = function test(x) { +//│ let Foo2, tmp; +//│ Foo2 = function Foo(a1, b1) { //│ return new Foo.class(a1, b1); //│ }; -//│ Foo.class = class Foo { +//│ Foo2.class = class Foo1 { //│ constructor(a, b) { //│ this.a = a; //│ this.b = b; //│ } -//│ toString() { return "Foo(" + this.a + ", " + this.b + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.a) + ", " + globalThis.Predef.render(this.b) + ")"; } //│ }; //│ tmp = x + 1; -//│ return Foo(x, tmp); +//│ return Foo2(x, tmp); //│ }; //│ null test(123) -//│ = Foo { a: 123, b: 124 } +//│ = Foo1 { a: 123, b: 124 } // * Forgot to pass the arg: diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index 7cdf362bb..12f0f1303 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -119,8 +119,8 @@ fun f(x) = if x is None then "ok" else log("oops") //│ JS (unsanitized): -//│ let f9; -//│ f9 = function f(x3) { +//│ let f4; +//│ f4 = function f(x3) { //│ let param04, x4, scrut1; //│ if (x3 instanceof Some1.class) { //│ param04 = x3.value; @@ -129,13 +129,13 @@ fun f(x) = if x is //│ if (scrut1 === true) { //│ return 42; //│ } else { -//│ return log1("oops"); +//│ return log("oops"); //│ } //│ } else { //│ if (x3 instanceof None1.class) { //│ return "ok"; //│ } else { -//│ return log1("oops"); +//│ return log("oops"); //│ } //│ } //│ }; @@ -162,8 +162,8 @@ fun f(x) = if x is Some(u) then u Pair(a, b) then a + b //│ JS (unsanitized): -//│ let f11; -//│ f11 = function f(x3) { +//│ let f5; +//│ f5 = function f(x3) { //│ let param04, param1, a, b, param05, u; //│ if (x3 instanceof Some1.class) { //│ param05 = x3.value; @@ -197,8 +197,8 @@ fun f(x) = log of if x is None then "ok" else "oops" //│ JS (unsanitized): -//│ let f13; -//│ f13 = function f(x3) { +//│ let f6; +//│ f6 = function f(x3) { //│ let param04, tmp11; //│ if (x3 instanceof Some1.class) { //│ param04 = x3.value; @@ -214,7 +214,7 @@ fun f(x) = log of if x is //│ tmp11 = "oops"; //│ } //│ } -//│ return log1(tmp11); +//│ return log(tmp11); //│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/Comma.mls b/hkmc2/shared/src/test/mlscript/codegen/Comma.mls index ef2745fc4..a4dba1a82 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Comma.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Comma.mls @@ -7,15 +7,15 @@ fun f() = console.log("ok"), 42 //│ JS (unsanitized): -//│ let f1; f1 = function f() { let tmp; tmp = globalThis.console.log("ok") ?? null; return 42; }; null +//│ let f; f = function f() { let tmp; tmp = globalThis.console.log("ok") ?? null; return 42; }; null fun f() = { console.log("ok"), 42 } //│ JS (unsanitized): -//│ let f3; f3 = function f() { let tmp; tmp = globalThis.console.log("ok") ?? null; return 42; }; null +//│ let f1; f1 = function f() { let tmp; tmp = globalThis.console.log("ok") ?? null; return 42; }; null fun f() = console.log("ok"), 42 //│ JS (unsanitized): -//│ let f5; f5 = function f() { return globalThis.console.log("ok") ?? null; }; 42 +//│ let f2; f2 = function f() { return globalThis.console.log("ok") ?? null; }; 42 //│ = 42 diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index ccd55948d..1f4bbfc93 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -44,23 +44,23 @@ z = 1 fun f() = 1 //│ JS (unsanitized): -//│ let f1; f1 = function f() { return 1; }; null +//│ let f; f = function f() { return 1; }; null f //│ JS (unsanitized): -//│ f1 +//│ f //│ = [Function: f] let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f2; f2 = (x1) => { return x1 + 1; }; null -//│ f = [Function: f2] +//│ let f1; f1 = (x1) => { return x1 + 1; }; null +//│ f = [Function: f1] f(1) //│ JS (unsanitized): -//│ f2(1) ?? null +//│ f1(1) ?? null //│ = 2 @@ -111,11 +111,11 @@ else fun f() = foo = 42 //│ JS (unsanitized): -//│ let f4; f4 = function f() { foo2 = 42; return null; }; null +//│ let f2; f2 = function f() { foo2 = 42; return null; }; null f() //│ JS (unsanitized): -//│ f4() +//│ f2() foo //│ JS (unsanitized): @@ -129,6 +129,6 @@ fun f() = foo = 0 //│ ║ l.127: fun f() = foo = 0 //│ ╙── ^ //│ JS (unsanitized): -//│ let f6; f6 = function f() { return foo2; }; null +//│ let f3; f3 = function f() { return foo2; }; null diff --git a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls index a3ab1d14d..93208baf4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/EarlyReturn.mls @@ -21,12 +21,12 @@ fun f(x) = return 0 x + 1 //│ JS (unsanitized): -//│ let f3; -//│ f3 = function f(x) { +//│ let f1; +//│ f1 = function f(x) { //│ let scrut, tmp, tmp1; //│ scrut = x < 0; //│ if (scrut === true) { -//│ tmp = log1("whoops"); +//│ tmp = log("whoops"); //│ return 0; //│ } else { //│ tmp1 = null; diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls index 1f8a53e7e..43a2ba01d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls @@ -22,17 +22,17 @@ fun test(a) = h(d) Inner(42) //│ JS (unsanitized): -//│ let test3; -//│ test3 = function test(a) { -//│ let Inner; -//│ Inner = function Inner(b1) { +//│ let test1; +//│ test1 = function test(a) { +//│ let Inner1; +//│ Inner1 = function Inner(b1) { //│ return new Inner.class(b1); //│ }; -//│ Inner.class = class Inner { +//│ Inner1.class = class Inner { //│ constructor(b) { //│ this.b = b; //│ let tmp; -//│ tmp = log1(a); +//│ tmp = log(a); //│ } //│ f(c) { //│ return [ @@ -54,9 +54,9 @@ fun test(a) = //│ }; //│ return h(d); //│ } -//│ toString() { return "Inner(" + this.b + ")"; } +//│ toString() { return "Inner(" + globalThis.Predef.render(this.b) + ")"; } //│ }; -//│ return Inner(42); +//│ return Inner1(42); //│ }; //│ null @@ -79,13 +79,13 @@ fun test(a) = print of [a, b] [C1(1), C2(2)] //│ JS (unsanitized): -//│ let test5; -//│ test5 = function test(a) { -//│ let C1, C2, tmp1, tmp2; -//│ C1 = function C1(b1) { +//│ let test2; +//│ test2 = function test(a) { +//│ let C11, C21, tmp1, tmp2; +//│ C11 = function C1(b1) { //│ return new C1.class(b1); //│ }; -//│ C1.class = class C1 { +//│ C11.class = class C1 { //│ constructor(b) { //│ this.b = b; //│ Predef.print([ @@ -93,12 +93,12 @@ fun test(a) = //│ this.b //│ ]) //│ } -//│ toString() { return "C1(" + this.b + ")"; } +//│ toString() { return "C1(" + globalThis.Predef.render(this.b) + ")"; } //│ }; -//│ C2 = function C2(b1) { +//│ C21 = function C2(b1) { //│ return new C2.class(b1); //│ }; -//│ C2.class = class C2 { +//│ C21.class = class C2 { //│ constructor(b) { //│ this.b = b; //│ Predef.print([ @@ -106,10 +106,10 @@ fun test(a) = //│ this.b //│ ]) //│ } -//│ toString() { return "C2(" + this.b + ")"; } +//│ toString() { return "C2(" + globalThis.Predef.render(this.b) + ")"; } //│ }; -//│ tmp1 = C1(1); -//│ tmp2 = C2(2); +//│ tmp1 = C11(1); +//│ tmp2 = C21(2); //│ return [ //│ tmp1, //│ tmp2 @@ -118,8 +118,8 @@ fun test(a) = //│ null test(123) -//│ > 123,1 -//│ > 123,2 +//│ > [123, 1] +//│ > [123, 2] //│ = [ C1 { b: 1 }, C2 { b: 2 } ] @@ -154,7 +154,7 @@ Foo(123) //│ tmp1 = bar(); //│ return baz(); //│ } -//│ toString() { return "Foo(" + this.a + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.a) + ")"; } //│ }; //│ Foo1(123) //│ = Foo { a: 123 } @@ -189,7 +189,7 @@ Bar(1) //│ return bar(); //│ }; //│ } -//│ toString() { return "Bar(" + this.x + ")"; } +//│ toString() { return "Bar(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ Bar1(1) //│ = Bar { x: 1 } diff --git a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls index f269fc68c..d47a737ec 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls @@ -86,7 +86,7 @@ outerfun(100)(200) :showRepl fun test1(x) = test2(x) fun test2(y) = y + 1 -//│ REPL> Sending: let test21, test11;try { test11 = function test1(...args) { globalThis.Predef.checkArgs("test1", 1, true, args.length); let x = args[0]; return test21(x); }; test21 = function test2(...args) { globalThis.Predef.checkArgs("test2", 1, true, args.length); let y = args[0]; return y + 1; }; null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let test2, test1;try { test1 = function test1(...args) { globalThis.Predef.checkArgs("test1", 1, true, args.length); let x = args[0]; return test2(x); }; test2 = function test2(...args) { globalThis.Predef.checkArgs("test2", 1, true, args.length); let y = args[0]; return y + 1; }; null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > null //│ REPL> Parsed: diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls index 7eb5133d8..ce7256045 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunctionsThis.mls @@ -9,7 +9,7 @@ val x = 2 fun foo() = x + 1 //│ JS (unsanitized): -//│ let foo1, x1; foo1 = function foo() { return x1 + 1; }; x1 = 2; null +//│ let foo, x; foo = function foo() { return x + 1; }; x = 2; null //│ x = 2 :sjs @@ -20,8 +20,8 @@ class Test with //│ Test1 = class Test { //│ constructor() { //│ let tmp; -//│ tmp = foo1(); -//│ log1(tmp) +//│ tmp = foo(); +//│ log(tmp) //│ } //│ toString() { return "Test"; } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index e1076443d..97d77acf2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -4,14 +4,14 @@ :sjs fun t = 42 //│ JS (unsanitized): -//│ let t1; t1 = function t() { return 42; }; null +//│ let t; t = function t() { return 42; }; null :expect 42 :sjs t //│ JS (unsanitized): -//│ let tmp; tmp = t1(); tmp +//│ let tmp; tmp = t(); tmp //│ = 42 @@ -37,8 +37,8 @@ fun test() = 42 whoops + whoops //│ JS (unsanitized): -//│ let test1; -//│ test1 = function test() { +//│ let test; +//│ test = function test() { //│ let whoops, tmp1, tmp2; //│ whoops = function whoops() { //│ let tmp3; @@ -67,22 +67,21 @@ module T with val d = this.p //│ JS (unsanitized): //│ let T1; -//│ const T$class = class T { -//│ constructor() { -//│ this.a = this.t; -//│ this.b = this.t; -//│ this.c = this.p; -//│ this.d = this.p; +//│ T1 = class T { +//│ static { +//│ this.a = T.t; +//│ this.b = T.t; +//│ this.c = T.p; +//│ this.d = T.p; //│ } -//│ t() { +//│ static t() { //│ return 1; //│ } -//│ get p() { +//│ static get p() { //│ return 2; //│ } -//│ toString() { return "T"; } -//│ }; T1 = new T$class; -//│ T1.class = T$class; +//│ static toString() { return "T"; } +//│ }; //│ null @@ -104,14 +103,13 @@ module M with fun t = 0 //│ JS (unsanitized): //│ let M1; -//│ const M$class = class M { -//│ constructor() {} -//│ get t() { +//│ M1 = class M { +//│ static {} +//│ static get t() { //│ return 0; //│ } -//│ toString() { return "M"; } -//│ }; M1 = new M$class; -//│ M1.class = M$class; +//│ static toString() { return "M"; } +//│ }; //│ null @@ -127,8 +125,8 @@ fun test() = fun whoops = 42 whoops //│ JS (unsanitized): -//│ let test3; -//│ test3 = function test() { +//│ let test1; +//│ test1 = function test() { //│ let whoops, tmp1; //│ whoops = function whoops() { //│ return 42; @@ -158,7 +156,7 @@ fun bar() = fun baz() = 42 baz //│ JS (unsanitized): -//│ let bar1; bar1 = function bar() { let baz; baz = function baz() { return 42; }; return baz; }; null +//│ let bar; bar = function bar() { let baz; baz = function baz() { return 42; }; return baz; }; null :sjs @@ -167,8 +165,8 @@ fun baz() = fun z = 2 (x, y) => x + y + w + z //│ JS (unsanitized): -//│ let baz1; -//│ baz1 = function baz() { +//│ let baz; +//│ baz = function baz() { //│ let w, z; //│ w = function w() { //│ return 1; @@ -200,8 +198,8 @@ fun a() = b + d c //│ JS (unsanitized): -//│ let a1; -//│ a1 = function a() { +//│ let a; +//│ a = function a() { //│ let b, c; //│ b = function b() { //│ return 1; @@ -232,8 +230,8 @@ fun b() = c d //│ JS (unsanitized): -//│ let b1; -//│ b1 = function b() { +//│ let b; +//│ b = function b() { //│ let c, d; //│ c = function c() { //│ return 1; @@ -263,8 +261,8 @@ fun c() = e + f d //│ JS (unsanitized): -//│ let c1; -//│ c1 = function c() { +//│ let c; +//│ c = function c() { //│ let d, f, tmp4; //│ f = function f() { //│ return 1; @@ -303,7 +301,7 @@ class Foo(x) with //│ get oops() { //│ return this.x; //│ } -//│ toString() { return "Foo(" + this.x + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls index 9b74eaad3..da520d4be 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls @@ -29,8 +29,8 @@ fun foo() = if false then 0 foo() //│ JS (unsanitized): -//│ let foo1; -//│ foo1 = function foo() { +//│ let foo; +//│ foo = function foo() { //│ let scrut1; //│ scrut1 = false; //│ if (scrut1 === true) { @@ -39,7 +39,7 @@ foo() //│ throw new globalThis.Error("match error"); //│ } //│ }; -//│ foo1() +//│ foo() //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'checkArgs') diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 8dc3d181a..125306ab0 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -41,12 +41,12 @@ Test.foo() :sjs val Test = "oops" //│ JS (unsanitized): -//│ let Test3; Test3 = "oops"; null +//│ let Test2; Test2 = "oops"; null //│ Test = 'oops' :re Test.foo() -//│ ═══[RUNTIME ERROR] TypeError: Test3.foo is not a function +//│ ═══[RUNTIME ERROR] TypeError: Test2.foo is not a function :sjs @@ -61,21 +61,26 @@ f() //│ x = 2 -:sjs +:ssjs +:e module Test with val x = 1 let x = 2 -//│ JS (unsanitized): -//│ let Test5; -//│ const Test$class1 = class Test { -//│ #x; -//│ constructor() { +//│ ╔══[ERROR] Name 'x' is already used +//│ ║ l.68: let x = 2 +//│ ║ ^^^^^ +//│ ╟── by a member declared in the same block +//│ ║ l.67: val x = 1 +//│ ╙── ^^^^^ +//│ JS: +//│ Test4 = class Test3 { +//│ static #x; +//│ static { //│ this.x = 1; //│ this.#x = 2; //│ } -//│ toString() { return "Test"; } -//│ }; Test5 = new Test$class1; -//│ Test5.class = Test$class1; +//│ static toString() { return "Test"; } +//│ }; //│ null Test.x @@ -120,7 +125,7 @@ class Cls(x) with //│ get bar() { //│ return this.#x1; //│ } -//│ toString() { return "Cls(" + this.x + ")"; } +//│ toString() { return "Cls(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ null @@ -150,6 +155,45 @@ fun foo() = A foo() -//│ = A { class: [class A] } +//│ = [class A1] + + +:sjs +module Whoops with + val v = this + fun f() = "Hello" + module Whoops with + val w = this + fun g() = f() +//│ JS (unsanitized): +//│ let Whoops2; +//│ Whoops2 = class Whoops { +//│ static { +//│ this.v = Whoops; +//│ this.Whoops = class Whoops1 { +//│ static { +//│ this.w = Whoops1; +//│ } +//│ static g() { +//│ return Whoops.f(); +//│ } +//│ static toString() { return "Whoops"; } +//│ }; +//│ } +//│ static f() { +//│ return "Hello"; +//│ } +//│ static toString() { return "Whoops"; } +//│ }; +//│ null + +Whoops.f() +//│ = 'Hello' + +Whoops.Whoops +//│ = [class Whoops1] { w: [Circular *1] } + +Whoops.Whoops.g() +//│ = 'Hello' diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index 8b2513a08..8b4cd2d80 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -12,7 +12,7 @@ if true then 1 else 0 :sjs let f = x => if x then log("ok") else log("ko") //│ JS (unsanitized): -//│ let f; f = (x) => { if (x === true) { return log1("ok"); } else { return log1("ko"); } }; null +//│ let f; f = (x) => { if (x === true) { return log("ok"); } else { return log("ko"); } }; null //│ f = [Function: f] f(true) @@ -34,7 +34,7 @@ let f = x => log((if x then "ok" else "ko") + "!") //│ tmp1 = "ko"; //│ } //│ tmp2 = tmp1 + "!"; -//│ return log1(tmp2); +//│ return log(tmp2); //│ }; //│ f1 = tmp; //│ null @@ -56,7 +56,7 @@ let f = x => log((if x and x then "ok" else "ko") + "!") //│ tmp2 = "ko"; //│ } //│ tmp3 = tmp2 + "!"; -//│ return log1(tmp3); +//│ return log(tmp3); //│ }; //│ f2 = tmp1; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls index 5bb59e028..bdf9aedc5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportMLsJS.mls @@ -2,11 +2,10 @@ import "../../mlscript-compile/Option.mjs" -//│ > Option { +//│ > [class Option] { //│ > Some: [Function: Some] { class: [class Some] }, //│ > None: None { class: [class None] }, -//│ > Both: [Function: Both] { class: [class Both] }, -//│ > class: [class Option] +//│ > Both: [Function: Both] { class: [class Both] } //│ Option = } diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index 500b17e7e..d7261dac2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -11,13 +11,13 @@ fun foo() = "a" ~ "b" ~ "c" foo() //│ JS (unsanitized): -//│ let foo1; -//│ foo1 = function foo() { +//│ let foo; +//│ foo = function foo() { //│ let tmp; //│ tmp = M1.concat("a", "b"); //│ return M1.concat(tmp, "c"); //│ }; -//│ foo1() +//│ foo() //│ = 'abc' let name = "_" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls index cc3dfe1e4..aa9cebdae 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls @@ -15,7 +15,7 @@ x => :todo (acc, _) => acc //│ JS (unsanitized): -//│ (acc, $_) => { return acc; } +//│ (acc, _) => { return acc; } //│ = [Function (anonymous)] diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 56b1a7da2..93becbae7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -12,25 +12,25 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f1, g1, g$1; -//│ g$1 = function g$(used1, used2, g$_arg) { +//│ let f, g, g$; +//│ g$ = function g$(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; //│ return used1 + used2; //│ }; -//│ g1 = function g(used1, used2) { -//│ return (g$_arg) => { -//│ return g$1(used1, used2, g$_arg); +//│ g = function g(used1, used2) { +//│ return (g_arg) => { +//│ return g$(used1, used2, g_arg); //│ }; //│ }; -//│ f1 = function f(used1, unused1) { +//│ f = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g$1(used1, used2, used2); +//│ tmp = g$(used1, used2, used2); //│ return tmp + unused2; //│ }; -//│ f1(1, 2) +//│ f(1, 2) //│ = 5 :expect 9 @@ -44,30 +44,30 @@ fun f(used1, unused1) = g(used2)(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f3, g3, g$3; -//│ g$3 = function g$(used1, used2, g$_arg) { -//│ return (g$_arg2) => { +//│ let f1, g1, g$1; +//│ g$1 = function g$(used1, used2, g_arg) { +//│ return (g_arg2) => { //│ let used3, tmp, tmp1; //│ used3 = 2; //│ tmp = used1 + used2; -//│ tmp1 = tmp + g$_arg; -//│ return tmp1 + g$_arg2; +//│ tmp1 = tmp + g_arg; +//│ return tmp1 + g_arg2; //│ }; //│ }; -//│ g3 = function g(used1, used2) { -//│ return (g$_arg) => { -//│ return g$3(used1, used2, g$_arg); +//│ g1 = function g(used1, used2) { +//│ return (g_arg) => { +//│ return g$1(used1, used2, g_arg); //│ }; //│ }; -//│ f3 = function f(used1, unused1) { +//│ f1 = function f(used1, unused1) { //│ let used2, unused2, tmp, tmp1; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g$3(used1, used2, used2); +//│ tmp = g$1(used1, used2, used2); //│ tmp1 = tmp(used2) ?? null; //│ return tmp1 + unused2; //│ }; -//│ f3(1, 2) +//│ f1(1, 2) //│ = 9 :expect 9 @@ -94,26 +94,26 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f7, g7, g$7; -//│ g$7 = function g$(used1, used2, g$_arg) { +//│ let f3, g3, g$3; +//│ g$3 = function g$(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; //│ return used1 + used2; //│ }; -//│ g7 = function g(used1, used2) { -//│ return (g$_arg) => { -//│ return g$7(used1, used2, g$_arg); +//│ g3 = function g(used1, used2) { +//│ return (g_arg) => { +//│ return g$3(used1, used2, g_arg); //│ }; //│ }; -//│ f7 = function f(used1, unused1) { +//│ f3 = function f(used1, unused1) { //│ let used2, unused2, foo, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ foo = g7(used1, used2) ?? null; +//│ foo = g3(used1, used2) ?? null; //│ tmp = foo(used2) ?? null; //│ return tmp + unused2; //│ }; -//│ f7(1, 2) +//│ f3(1, 2) //│ = 5 // don't touch g if not needed @@ -126,18 +126,18 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f9, g9; -//│ g9 = function g(g$_arg) { -//│ return g$_arg + 1; +//│ let f4, g4; +//│ g4 = function g(g_arg) { +//│ return g_arg + 1; //│ }; -//│ f9 = function f(used1, unused1) { +//│ f4 = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g9(used2); +//│ tmp = g4(used2); //│ return tmp + unused2; //│ }; -//│ f9(1, 2) +//│ f4(1, 2) //│ = 5 @@ -150,8 +150,8 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f11, g11, g$9; -//│ g$9 = function g$(a1, a2, a3, a4, a5, a6) { +//│ let f5, g5, g$4; +//│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; @@ -159,17 +159,17 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6; //│ }; -//│ g11 = function g(a1, a2, a3, a4, a5, a6) { +//│ g5 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ return g$9(a1, a2, a3, a4, a5, a6); +//│ return g$4(a1, a2, a3, a4, a5, a6); //│ }; //│ }; -//│ f11 = function f(a1, a2, a3, a4, a5, a6) { +//│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$9(a1, a2, a3, a4, a5, a6); +//│ tmp = g$4(a1, a2, a3, a4, a5, a6); //│ return tmp; //│ }; -//│ f11(1, 2, 3, 4, 5, 6) +//│ f5(1, 2, 3, 4, 5, 6) //│ = 21 :expect '01' @@ -188,22 +188,22 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f13, Tuple1, g13, h1, ret, f14, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$1, g$11, f$capture1; -//│ g$11 = function g$(f$capture2) { +//│ let f6, Tuple1, g6, h, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; +//│ g$5 = function g$(f$capture2) { //│ f$capture2.a0$ = 1; //│ return null; //│ }; -//│ g13 = function g(f$capture2) { +//│ g6 = function g(f$capture2) { //│ return () => { -//│ return g$11(f$capture2); +//│ return g$5(f$capture2); //│ }; //│ }; -//│ h$1 = function h$(f$capture2) { +//│ h$ = function h$(f$capture2) { //│ return f$capture2.a0$; //│ }; -//│ h1 = function h(f$capture2) { +//│ h = function h(f$capture2) { //│ return () => { -//│ return h$1(f$capture2); +//│ return h$(f$capture2); //│ }; //│ }; //│ f$capture1 = function f$capture(a0$1) { @@ -213,14 +213,14 @@ x + y //│ constructor(a0$) { //│ this.a0$ = a0$; //│ } -//│ toString() { return "f$capture(" + this.a0$ + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.a0$) + ")"; } //│ }; -//│ f13 = function f() { +//│ f6 = function f() { //│ let capture, g$this, h$this; //│ capture = new f$capture1(null); //│ capture.a0$ = 0; -//│ g$this = g13(capture) ?? null; -//│ h$this = h1(capture) ?? null; +//│ g$this = g6(capture) ?? null; +//│ h$this = h(capture) ?? null; //│ return Tuple1(g$this, h$this); //│ }; //│ Tuple1 = function Tuple(a1, b1) { @@ -231,16 +231,16 @@ x + y //│ this.a = a; //│ this.b = b; //│ } -//│ toString() { return "Tuple(" + this.a + ", " + this.b + ")"; } +//│ toString() { return "Tuple(" + globalThis.Predef.render(this.a) + ", " + globalThis.Predef.render(this.b) + ")"; } //│ }; -//│ tmp = f13(); +//│ tmp = f6(); //│ ret = tmp; -//│ f14 = ret.a; +//│ f11 = ret.a; //│ f21 = ret.b; //│ tmp1 = f21() ?? null; //│ tmp2 = tmp1.toString() ?? null; //│ x = tmp2; -//│ tmp3 = f14() ?? null; +//│ tmp3 = f11() ?? null; //│ tmp4 = f21() ?? null; //│ tmp5 = tmp4.toString() ?? null; //│ y = tmp5; @@ -281,11 +281,11 @@ a.toString() + b + c + d //│ c = 10 //│ d = 110 //│ g2 = [Function (anonymous)] -//│ gRet = Tuple { a: [Function (anonymous)], b: [Function (anonymous)] } +//│ gRet = Tuple2 { a: [Function (anonymous)], b: [Function (anonymous)] } //│ hFun = [Function (anonymous)] //│ iFun = [Function (anonymous)] -//│ > Tuple { -//│ > a: Tuple { a: [Function (anonymous)], b: [Function (anonymous)] }, +//│ > Tuple2 { +//│ > a: Tuple2 { a: [Function (anonymous)], b: [Function (anonymous)] }, //│ > b: [Function (anonymous)] //│ ret = } @@ -301,43 +301,43 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f18, g17, h5, h$5, g$13, f$capture5; -//│ g$13 = function g$(immutable, f$capture6) { +//│ let f8, g7, h2, h$2, g$6, f$capture5; +//│ g$6 = function g$(immutable, f$capture6) { //│ f$capture6.mutated0$ = 2; //│ return immutable + f$capture6.mutated0$; //│ }; -//│ g17 = function g(immutable, f$capture6) { +//│ g7 = function g(immutable, f$capture6) { //│ return () => { -//│ return g$13(immutable, f$capture6); +//│ return g$6(immutable, f$capture6); //│ }; //│ }; -//│ h$5 = function h$(immutable, f$capture6) { +//│ h$2 = function h$(immutable, f$capture6) { //│ return f$capture6.mutated0$; //│ }; -//│ h5 = function h(immutable, f$capture6) { +//│ h2 = function h(immutable, f$capture6) { //│ return () => { -//│ return h$5(immutable, f$capture6); +//│ return h$2(immutable, f$capture6); //│ }; //│ }; //│ f$capture5 = function f$capture(mutated0$1) { //│ return new f$capture.class(mutated0$1); //│ }; -//│ f$capture5.class = class f$capture { +//│ f$capture5.class = class f$capture4 { //│ constructor(mutated0$) { //│ this.mutated0$ = mutated0$; //│ } -//│ toString() { return "f$capture(" + this.mutated0$ + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.mutated0$) + ")"; } //│ }; -//│ f18 = function f(unused, immutable, mutated) { +//│ f8 = function f(unused, immutable, mutated) { //│ let a1, tmp21, tmp22, tmp23, capture; //│ capture = new f$capture5(mutated); -//│ tmp21 = g$13(immutable, capture); +//│ tmp21 = g$6(immutable, capture); //│ a1 = tmp21; -//│ tmp22 = h$5(immutable, capture); +//│ tmp22 = h$2(immutable, capture); //│ tmp23 = a1 + tmp22; //│ return tmp23 + unused; //│ }; -//│ f18(1, 2, 1000) +//│ f8(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -369,41 +369,41 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let f25, g25, h7, h$7, g$19; -//│ g$19 = function g$(arg, n) { +//│ let f12, g10, h3, h$3, g$9; +//│ g$9 = function g$(arg, n) { //│ let scrut, tmp21; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { //│ tmp21 = n - 1; -//│ return h$7(arg, tmp21); +//│ return h$3(arg, tmp21); //│ } //│ }; -//│ g25 = function g(arg) { +//│ g10 = function g(arg) { //│ return (n) => { -//│ return g$19(arg, n); +//│ return g$9(arg, n); //│ }; //│ }; -//│ h$7 = function h$(arg, n) { +//│ h$3 = function h$(arg, n) { //│ let scrut, tmp21; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg; //│ } else { //│ tmp21 = n - 1; -//│ return g$19(arg, tmp21); +//│ return g$9(arg, tmp21); //│ } //│ }; -//│ h7 = function h(arg) { +//│ h3 = function h(arg) { //│ return (n) => { -//│ return h$7(arg, n); +//│ return h$3(arg, n); //│ }; //│ }; -//│ f25 = function f(arg) { -//│ return h$7(arg, 5); +//│ f12 = function f(arg) { +//│ return h$3(arg, 5); //│ }; -//│ f25(2) +//│ f12(2) //│ = 2 :expect 1 @@ -430,32 +430,32 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let f31, Test1, g29, h9, tmp21, Test$ctor1, Test$1, g$23, h$9; -//│ h$9 = function h$(used3, used1) { +//│ let f15, Test1, g13, h4, tmp21, Test$ctor, Test$, g$11, h$4; +//│ h$4 = function h$(used3, used1) { //│ return used3; //│ }; -//│ h9 = function h(used3, used1) { +//│ h4 = function h(used3, used1) { //│ return () => { -//│ return h$9(used3, used1); +//│ return h$4(used3, used1); //│ }; //│ }; -//│ g$23 = function g$(used1, g$_arg) { +//│ g$11 = function g$(used1, g_arg) { //│ let used3, tmp22; //│ used3 = 2; -//│ tmp22 = h$9(used3, used1); +//│ tmp22 = h$4(used3, used1); //│ return used1 + tmp22; //│ }; -//│ g29 = function g(used1) { -//│ return (g$_arg) => { -//│ return g$23(used1, g$_arg); +//│ g13 = function g(used1) { +//│ return (g_arg) => { +//│ return g$11(used1, g_arg); //│ }; //│ }; -//│ Test$1 = function Test$(used1, a1) { +//│ Test$ = function Test$(used1, a1) { //│ let tmp22; //│ tmp22 = new Test1(a1); //│ return tmp22(used1); //│ }; -//│ Test$ctor1 = function Test$ctor(used1) { +//│ Test$ctor = function Test$ctor(used1) { //│ return (a1) => { //│ let tmp22; //│ tmp22 = new Test1(a1); @@ -478,14 +478,14 @@ f(1, 2).get() //│ get() { //│ return this.used1; //│ } -//│ toString() { return "Test(" + this.a + ")"; } +//│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; -//│ f31 = function f(used1, unused1) { +//│ f15 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; -//│ return Test$1(used1, unused1); +//│ return Test$(used1, unused1); //│ }; -//│ tmp21 = f31(1, 2); +//│ tmp21 = f15(1, 2); //│ tmp21.get() ?? null //│ = 1 @@ -548,41 +548,41 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f40, f41, g37, f$3, g$31, f$capture13; -//│ g$31 = function g$(f$capture14) { +//│ let f19, f20, g17, f$1, g$15, f$capture13; +//│ g$15 = function g$(f$capture14) { //│ return 1; //│ }; -//│ g37 = function g(f$capture14) { +//│ g17 = function g(f$capture14) { //│ return () => { -//│ return g$31(f$capture14); +//│ return g$15(f$capture14); //│ }; //│ }; -//│ f$3 = function f$(f$capture14) { +//│ f$1 = function f$(f$capture14) { //│ f$capture14.x0$ = 1; //│ return null; //│ }; -//│ f41 = function f(f$capture14) { +//│ f20 = function f(f$capture14) { //│ return () => { -//│ return f$3(f$capture14); +//│ return f$1(f$capture14); //│ }; //│ }; //│ f$capture13 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); //│ }; -//│ f$capture13.class = class f$capture { +//│ f$capture13.class = class f$capture12 { //│ constructor(x0$) { //│ this.x0$ = x0$; //│ } -//│ toString() { return "f$capture(" + this.x0$ + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f40 = function f() { +//│ f19 = function f() { //│ let tmp26, capture; //│ capture = new f$capture13(null); //│ capture.x0$ = 1; -//│ tmp26 = f$3(capture); +//│ tmp26 = f$1(capture); //│ return capture.x0$; //│ }; -//│ f40() +//│ f19() //│ = 1 // don't break private variables @@ -622,37 +622,37 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let g41, f43, h15, tmp28, f$5, h$15, g$capture1, f$capture15; -//│ h$15 = function h$(f$capture16, g$capture2) { +//│ let g19, f22, h7, tmp28, f$2, h$7, g$capture1, f$capture15; +//│ h$7 = function h$(f$capture16, g$capture2) { //│ f$capture16.k0$ = 5; //│ f$capture16.x1$ = 4; //│ return f$capture16.x1$ + g$capture2.y0$; //│ }; -//│ h15 = function h(f$capture16, g$capture2) { +//│ h7 = function h(f$capture16, g$capture2) { //│ return () => { -//│ return h$15(f$capture16, g$capture2); +//│ return h$7(f$capture16, g$capture2); //│ }; //│ }; //│ f$capture15 = function f$capture(k0$1, x1$1) { //│ return new f$capture.class(k0$1, x1$1); //│ }; -//│ f$capture15.class = class f$capture { +//│ f$capture15.class = class f$capture14 { //│ constructor(k0$, x1$) { //│ this.k0$ = k0$; //│ this.x1$ = x1$; //│ } -//│ toString() { return "f$capture(" + this.k0$ + ", " + this.x1$ + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.k0$) + ", " + globalThis.Predef.render(this.x1$) + ")"; } //│ }; -//│ f$5 = function f$(g$capture2, x1) { +//│ f$2 = function f$(g$capture2, x1) { //│ let capture; //│ capture = new f$capture15(null, x1); //│ capture.k0$ = 4; //│ g$capture2.y0$ = 2; //│ return capture.x1$; //│ }; -//│ f43 = function f(g$capture2) { +//│ f22 = function f(g$capture2) { //│ return (x1) => { -//│ return f$5(g$capture2, x1); +//│ return f$2(g$capture2, x1); //│ }; //│ }; //│ g$capture1 = function g$capture(y0$1) { @@ -662,14 +662,14 @@ g()(1) //│ constructor(y0$) { //│ this.y0$ = y0$; //│ } -//│ toString() { return "g$capture(" + this.y0$ + ")"; } +//│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } //│ }; -//│ g41 = function g() { +//│ g19 = function g() { //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f43(capture) ?? null; +//│ return f22(capture) ?? null; //│ }; -//│ tmp28 = g41(); +//│ tmp28 = g19(); //│ tmp28(1) ?? null //│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index a34ad67ec..45161a3d7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -7,31 +7,25 @@ :sjs module None //│ JS (unsanitized): -//│ let None1; -//│ const None$class = class None { -//│ constructor() {} -//│ toString() { return "None"; } -//│ }; None1 = new None$class; -//│ None1.class = None$class; -//│ null +//│ let None1; None1 = class None { static {} static toString() { return "None"; } }; null :sjs None //│ JS (unsanitized): //│ None1 -//│ = None { class: [class None] } +//│ = [class None] :sjs :re None() //│ JS (unsanitized): //│ None1() ?? null -//│ ═══[RUNTIME ERROR] TypeError: None1 is not a function +//│ ═══[RUNTIME ERROR] TypeError: Class constructor None cannot be invoked without 'new' :sjs new None //│ JS (unsanitized): -//│ new None1.class() +//│ new None1() //│ = None {} @@ -43,8 +37,8 @@ module M with val y = x + 1 //│ JS (unsanitized): //│ let M1; -//│ const M$class = class M { -//│ constructor() { +//│ M1 = class M { +//│ static { //│ let tmp; //│ this.C = class C { //│ constructor() {} @@ -55,15 +49,14 @@ module M with //│ }; //│ this.D.class = class D { //│ constructor() {} -//│ toString() { return "D(" + + ")"; } +//│ toString() { return "D(" + "" + ")"; } //│ }; //│ this.x = 1; -//│ tmp = this.x + 1; +//│ tmp = M.x + 1; //│ this.y = tmp; //│ } -//│ toString() { return "M"; } -//│ }; M1 = new M$class; -//│ M1.class = M$class; +//│ static toString() { return "M"; } +//│ }; //│ null M.C @@ -85,7 +78,7 @@ M.y :re M.oops //│ ╔══[ERROR] Module 'M' does not contain member 'oops' -//│ ║ l.86: M.oops +//│ ║ l.79: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' @@ -97,15 +90,7 @@ M.oops module M with val m: module M = M //│ JS (unsanitized): -//│ let M3; -//│ const M$class1 = class M { -//│ constructor() { -//│ this.m = M3; -//│ } -//│ toString() { return "M"; } -//│ }; M3 = new M$class1; -//│ M3.class = M$class1; -//│ null +//│ let M3; M3 = class M2 { static { this.m = M3; } static toString() { return "M"; } }; null :re // FIXME :sjs diff --git a/hkmc2/shared/src/test/mlscript/codegen/ObjectMethodDebinding.mls b/hkmc2/shared/src/test/mlscript/codegen/ObjectMethodDebinding.mls new file mode 100644 index 000000000..879e57cc9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/ObjectMethodDebinding.mls @@ -0,0 +1,47 @@ +:js + + +object Test with + fun bar = 1 + fun foo() = bar + 1 + +let f = Test.foo +//│ f = [Function: foo] + +:re +f() +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'bar') + + +// * TODO we should probably compile the above as: + +object Test with + fun bar = 1 + fun foo() = Test.bar + 1 + +let f = Test.foo +//│ f = [Function: foo] + +f() +//│ = 2 + + +// * Which also works well no matter where the object is defined: + +fun test() = + object Test with + fun bar = 1 + fun foo() = Test.bar + 1 + [Test] + +let f = test().0.foo +//│ f = [Function: foo] + +f() +//│ = 2 + + +// * However, it would disable open recursion upon object inheritance (eg `class C extends Test with ...`). +// * But it would be better to require open recursion to be explicitly indicated by `virtual` anyway. + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/OpenWildcard.mls b/hkmc2/shared/src/test/mlscript/codegen/OpenWildcard.mls index 70d0aebde..dd3ee7399 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OpenWildcard.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OpenWildcard.mls @@ -52,7 +52,7 @@ none() :sjs val Option = "Oops" //│ JS (unsanitized): -//│ let Option2; Option2 = "Oops"; null +//│ let Option1; Option1 = "Oops"; null //│ Option = 'Oops' :sjs @@ -78,7 +78,7 @@ Some :sjs None //│ JS (unsanitized): -//│ Option4.None +//│ Option3.None //│ = 123 diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index 65bd360b5..64c9ae9dc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -10,8 +10,8 @@ fun isDefined(x) = if x is Some then true None then false //│ JS (unsanitized): -//│ let isDefined1; -//│ isDefined1 = function isDefined(x) { +//│ let isDefined; +//│ isDefined = function isDefined(x) { //│ if (x instanceof Some1.class) { //│ return true; //│ } else { @@ -36,7 +36,7 @@ val isDefined = case Some(_) then true None then false //│ JS (unsanitized): -//│ let isDefined3, tmp1; +//│ let isDefined1, tmp1; //│ tmp1 = (caseScrut) => { //│ let param0; //│ if (caseScrut instanceof Some1.class) { @@ -50,7 +50,7 @@ val isDefined = case //│ } //│ } //│ }; -//│ isDefined3 = tmp1; +//│ isDefined1 = tmp1; //│ null //│ isDefined = [Function: tmp1] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls index e4c979c0f..1830d0f79 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls @@ -15,7 +15,7 @@ class Foo() //│ }; //│ Foo1.class = class Foo { //│ constructor() {} -//│ toString() { return "Foo(" + + ")"; } +//│ toString() { return "Foo(" + "" + ")"; } //│ }; //│ null @@ -41,23 +41,23 @@ class Foo(a) //│ Foo3 = function Foo(a1) { //│ return new Foo.class(a1); //│ }; -//│ Foo3.class = class Foo { +//│ Foo3.class = class Foo2 { //│ constructor(a) { //│ this.a = a; //│ } -//│ toString() { return "Foo(" + this.a + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.a) + ")"; } //│ }; //│ null Foo //│ JS (unsanitized): //│ Foo3 -//│ = [Function: Foo] { class: [class Foo] } +//│ = [Function: Foo] { class: [class Foo2] } Foo(1) //│ JS (unsanitized): //│ Foo3(1) -//│ = Foo { a: 1 } +//│ = Foo2 { a: 1 } Foo(1).a //│ JS (unsanitized): @@ -67,8 +67,8 @@ Foo(1).a fun foo(y) = Foo(y) foo(27) //│ JS (unsanitized): -//│ let foo1; foo1 = function foo(y) { return Foo3(y); }; foo1(27) -//│ = Foo { a: 27 } +//│ let foo; foo = function foo(y) { return Foo3(y); }; foo(27) +//│ = Foo2 { a: 27 } class Foo(a, b) @@ -77,29 +77,29 @@ class Foo(a, b) //│ Foo5 = function Foo(a1, b1) { //│ return new Foo.class(a1, b1); //│ }; -//│ Foo5.class = class Foo { +//│ Foo5.class = class Foo4 { //│ constructor(a, b) { //│ this.a = a; //│ this.b = b; //│ } -//│ toString() { return "Foo(" + this.a + ", " + this.b + ")"; } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.a) + ", " + globalThis.Predef.render(this.b) + ")"; } //│ }; //│ null let foo = Foo //│ JS (unsanitized): -//│ let foo2; foo2 = Foo5; null -//│ foo = [Function: Foo] { class: [class Foo] } +//│ let foo1; foo1 = Foo5; null +//│ foo = [Function: Foo] { class: [class Foo4] } let f = foo(1, 2) //│ JS (unsanitized): -//│ let f, tmp1; tmp1 = foo2(1, 2) ?? null; f = tmp1; null -//│ f = Foo { a: 1, b: 2 } +//│ let f, tmp1; tmp1 = foo1(1, 2) ?? null; f = tmp1; null +//│ f = Foo4 { a: 1, b: 2 } let f = new foo(1, 2) //│ JS (unsanitized): -//│ let f1, tmp2; tmp2 = new foo2(1, 2); f1 = tmp2; null -//│ f = Foo { a: 1, b: 2 } +//│ let f1, tmp2; tmp2 = new foo1(1, 2); f1 = tmp2; null +//│ f = Foo4 { a: 1, b: 2 } f.a //│ JS (unsanitized): @@ -114,7 +114,7 @@ f.b let f = Foo(1, 2) //│ JS (unsanitized): //│ let f2, tmp3; tmp3 = Foo5(1, 2); f2 = tmp3; null -//│ f = Foo { a: 1, b: 2 } +//│ f = Foo4 { a: 1, b: 2 } f.a //│ JS (unsanitized): @@ -128,10 +128,10 @@ f.b Foo(log(1), log(2)) //│ JS (unsanitized): -//│ let tmp4, tmp5; tmp4 = log1(1); tmp5 = log1(2); Foo5(tmp4, tmp5) +//│ let tmp4, tmp5; tmp4 = log(1); tmp5 = log(2); Foo5(tmp4, tmp5) //│ > 1 //│ > 2 -//│ = Foo { a: null, b: null } +//│ = Foo4 { a: null, b: null } class Inner(c) with @@ -145,12 +145,12 @@ class Inner(c) with //│ Inner1.class = class Inner { //│ constructor(c) { //│ this.c = c; -//│ log1(this.c) +//│ log(this.c) //│ } //│ i1(d) { //│ return this.c + d; //│ } -//│ toString() { return "Inner(" + this.c + ")"; } +//│ toString() { return "Inner(" + globalThis.Predef.render(this.c) + ")"; } //│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 94adacf81..3c8fb8709 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -12,7 +12,7 @@ f(2) :sjs let f = foo(1, _, _) //│ JS (unsanitized): -//│ let f1; f1 = ($_, $_1) => { return foo1(1, $_, $_1); }; null +//│ let f1; f1 = (_, _1) => { return foo(1, _, _1); }; null //│ f = [Function: f1] f(2, 3) @@ -151,13 +151,13 @@ _ - 2 <| 1 :sjs 1 . _ - 2 //│ JS (unsanitized): -//│ (($_) => { return Predef.passTo(1, $_); }) - 2 +//│ ((_) => { return Predef.passTo(1, _); }) - 2 //│ = NaN :sjs 1 . (_ - 2)() //│ JS (unsanitized): -//│ let tmp7; tmp7 = Predef.passTo(1, ($_) => { return $_ - 2; }); tmp7() ?? null +//│ let tmp7; tmp7 = Predef.passTo(1, (_) => { return _ - 2; }); tmp7() ?? null //│ = -1 1 . (_ - _)(2) diff --git a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls index 76f823f3e..b42ac581c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls @@ -46,7 +46,7 @@ Foo() class Foo with { log("hi") } log("ok") //│ JS (unsanitized): -//│ let Foo3; Foo3 = class Foo { constructor() { log1("hi") } toString() { return "Foo"; } }; log1("ok") +//│ let Foo3; Foo3 = class Foo2 { constructor() { log("hi") } toString() { return "Foo"; } }; log("ok") //│ > ok fun test() = @@ -54,37 +54,37 @@ fun test() = log("ok") Foo //│ JS (unsanitized): -//│ let test1; -//│ test1 = function test() { -//│ let Foo4, tmp; -//│ Foo4 = class Foo { +//│ let test; +//│ test = function test() { +//│ let Foo5, tmp; +//│ Foo5 = class Foo4 { //│ constructor() { -//│ log1("hi") +//│ log("hi") //│ } //│ toString() { return "Foo"; } //│ }; -//│ tmp = log1("ok"); -//│ return Foo4; +//│ tmp = log("ok"); +//│ return Foo5; //│ }; //│ null let t = test() //│ JS (unsanitized): -//│ let t, tmp; tmp = test1(); t = tmp; null +//│ let t, tmp; tmp = test(); t = tmp; null //│ > ok -//│ t = [class Foo] +//│ t = [class Foo4] new t //│ JS (unsanitized): //│ new t() //│ > hi -//│ = Foo {} +//│ = Foo4 {} new t() //│ JS (unsanitized): //│ new t() //│ > hi -//│ = Foo {} +//│ = Foo4 {} class Foo with @@ -92,8 +92,8 @@ class Foo with let y = x + 1 fun z() = y + x //│ JS (unsanitized): -//│ let Foo5; -//│ Foo5 = class Foo { +//│ let Foo6; +//│ Foo6 = class Foo5 { //│ #y; //│ constructor() { //│ let tmp1; @@ -118,8 +118,8 @@ class Foo with fun z2() = 6 log("hello") //│ JS (unsanitized): -//│ let Foo7; -//│ Foo7 = class Foo { +//│ let Foo8; +//│ Foo8 = class Foo7 { //│ #y1; //│ #y2; //│ constructor() { @@ -127,7 +127,7 @@ class Foo with //│ this.x2 = 2; //│ this.#y1 = 3; //│ this.#y2 = 4; -//│ log1("hello") +//│ log("hello") //│ } //│ z1() { //│ return 5; @@ -145,8 +145,8 @@ class Foo with fun foo(y) = x + y fun bar(z) = foo(z) + 1 //│ JS (unsanitized): -//│ let Foo9; -//│ Foo9 = class Foo { +//│ let Foo10; +//│ Foo10 = class Foo9 { //│ constructor() { //│ this.x = 1; //│ } @@ -168,32 +168,55 @@ log(a.foo(1)) log(a.bar(1)) //│ JS (unsanitized): //│ let a, tmp1, tmp2, tmp3, tmp4, tmp5; -//│ tmp1 = new Foo9(); +//│ tmp1 = new Foo10(); //│ a = tmp1; -//│ tmp2 = log1(a.x); +//│ tmp2 = log(a.x); //│ tmp3 = a.foo(1) ?? null; -//│ tmp4 = log1(tmp3); +//│ tmp4 = log(tmp3); //│ tmp5 = a.bar(1) ?? null; -//│ log1(tmp5) +//│ log(tmp5) //│ > 1 //│ > 2 //│ > 3 -//│ a = Foo { x: 1 } - +//│ a = Foo9 { x: 1 } +:e +class Foo with + val x = 1 + val x = 2 +//│ ╔══[ERROR] Multiple definitions of symbol 'x' +//│ ╟── defined here +//│ ║ l.187: val x = 1 +//│ ║ ^^^^^ +//│ ╟── defined here +//│ ║ l.188: val x = 2 +//│ ╙── ^^^^^ +//│ JS (unsanitized): +//│ let Foo12; +//│ Foo12 = class Foo11 { +//│ constructor() { +//│ this.x = 1; +//│ this.x = 2; +//│ } +//│ toString() { return "Foo"; } +//│ }; +//│ null -// ——— TODO ——— - - -// FIXME reject duplicated member names +:e class Foo with val x = 1 let x = 2 -//│ JS (unsanitized): -//│ let Foo11; -//│ Foo11 = class Foo { +//│ ╔══[ERROR] Name 'x' is already used +//│ ║ l.210: let x = 2 +//│ ║ ^^^^^ +//│ ╟── by a member declared in the same block +//│ ║ l.209: val x = 1 +//│ ╙── ^^^^^ +//│ JS (unsanitized): +//│ let Foo14; +//│ Foo14 = class Foo13 { //│ #x; //│ constructor() { //│ this.x = 1; @@ -204,16 +227,22 @@ class Foo with //│ null + + + +// ——— TODO ——— + + :fixme class Foo with val x = 1 //│ ╔══[PARSE ERROR] Expected block after type declaration body; found 'val' keyword instead -//│ ║ l.208: class Foo with val x = 1 +//│ ║ l.237: class Foo with val x = 1 //│ ╙── ^^^ //│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//│ ║ l.208: class Foo with val x = 1 +//│ ║ l.237: class Foo with val x = 1 //│ ╙── ^ //│ ╔══[ERROR] Illegal juxtaposition right-hand side. -//│ ║ l.208: class Foo with val x = 1 +//│ ║ l.237: class Foo with val x = 1 //│ ╙── ^ //│ JS (unsanitized): //│ /* error */ diff --git a/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls b/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls index 8c03a41b6..65597c3cc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PredefUsage.mls @@ -11,6 +11,9 @@ not(false) print(12) //│ > 12 +12 |> print(_) +//│ > 12 + :sjs 12 |> print //│ JS (unsanitized): diff --git a/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls new file mode 100644 index 000000000..f0ca0640d --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls @@ -0,0 +1,96 @@ +:js + + +:sjs +fun foo() = if false do foo() +//│ JS (unsanitized): +//│ let foo; +//│ foo = function foo() { +//│ let scrut; +//│ scrut = false; +//│ if (scrut === true) { +//│ return foo(); +//│ } else { +//│ return null; +//│ } +//│ }; +//│ null + +:sjs +fun foo() = foo() +//│ JS (unsanitized): +//│ let foo1; foo1 = function foo() { return foo1(); }; null + + +:sjs +class Foo(x) with + class Bar with + val y = x +//│ JS (unsanitized): +//│ let Foo1; +//│ Foo1 = function Foo(x1) { +//│ return new Foo.class(x1); +//│ }; +//│ Foo1.class = class Foo { +//│ constructor(x) { +//│ this.x = x; +//│ const this$Foo = this; +//│ this.Bar = class Bar { +//│ constructor() { +//│ this.y = this$Foo.x; +//│ } +//│ toString() { return "Bar"; } +//│ }; +//│ } +//│ toString() { return "Foo(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; +//│ null + + +:sjs +fun foo() = + fun bar() = bar() + bar() +//│ JS (unsanitized): +//│ let foo2; +//│ foo2 = function foo() { +//│ let bar; +//│ bar = function bar() { +//│ return bar(); +//│ }; +//│ return bar(); +//│ }; +//│ null + + +:sjs +let f = 1 +do + fun f = f + () +//│ JS (unsanitized): +//│ let f, f1; f = 1; f1 = function f() { let tmp; tmp = f1(); return tmp; }; null +//│ f = 1 + +:sjs +:e +let foo = 1 +fun foo(x) = foo +//│ ╔══[ERROR] Name 'foo' is already used +//│ ║ l.77: let foo = 1 +//│ ║ ^^^^^^^ +//│ ╟── by a member declared in the same block +//│ ║ l.78: fun foo(x) = foo +//│ ╙── ^^^^^^^^^^^^ +//│ JS (unsanitized): +//│ let foo3, foo4; foo3 = function foo(x) { return foo4; }; foo4 = 1; null +//│ foo = 1 + +:sjs +:re +foo(1) +//│ JS (unsanitized): +//│ foo4(1) ?? null +//│ ═══[RUNTIME ERROR] TypeError: foo4 is not a function + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls index fe25de0a8..1f6855d88 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls @@ -7,7 +7,7 @@ val res: Int :showRepl fun res() = 1 -//│ REPL> Sending: let res1;try { res1 = function res(...args) { globalThis.Predef.checkArgs("res", 0, true, args.length); return 1; }; null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let res;try { res = function res(...args) { globalThis.Predef.checkArgs("res", 0, true, args.length); return 1; }; null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > null //│ REPL> Parsed: @@ -24,7 +24,7 @@ fun res() = 1 :showRepl res -//│ REPL> Sending: try { res1 } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: try { res } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > [Function: res] //│ REPL> Parsed: @@ -54,7 +54,7 @@ let x = 1, log(x), x :showRepl let x = 1, log(x), x -//│ REPL> Sending: let x1, tmp2;try { x1 = 1; tmp2 = log1(x1); x1 } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let x1, tmp2;try { x1 = 1; tmp2 = log(x1); x1 } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > 1 //│ > 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index c196d98bf..5846227b0 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -6,13 +6,13 @@ fun f(x, y) = x + y f(2, 3) //│ JS: -//│ f1 = function f(...args) { +//│ f = function f(...args) { //│ globalThis.Predef.checkArgs("f", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y; //│ }; -//│ f1(2, 3) +//│ f(2, 3) //│ = 5 @@ -22,14 +22,14 @@ f(2, 3) fun f2(x, y) = x + y f2(2) //│ JS: -//│ f21 = function f2(x, y) { return x + y; }; f21(2) +//│ f2 = function f2(x, y) { return x + y; }; f2(2) //│ = NaN :ssjs :re f(2) //│ JS: -//│ f1(2) +//│ f(2) //│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 @@ -38,7 +38,7 @@ f(2) fun f(x)(y, z) = x + y + z f(3)(4) //│ JS: -//│ f4 = function f(...args) { +//│ f1 = function f(...args) { //│ globalThis.Predef.checkArgs("f", 1, true, args.length); //│ let x = args[0]; //│ return (...args1) => { @@ -50,7 +50,7 @@ f(3)(4) //│ return tmp1 + z; //│ }; //│ }; -//│ tmp = f4(3); +//│ tmp = f1(3); //│ tmp(4) ?? null //│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 @@ -60,7 +60,7 @@ f(3)(4) :noSanityCheck let f = (x, y) => x + y in f(2) //│ JS: -//│ f5 = (x, y) => { return x + y; }; f5(2) ?? null +//│ f3 = (x, y) => { return x + y; }; f3(2) ?? null //│ = NaN :ssjs @@ -68,15 +68,15 @@ let f = (x, y) => x + y in f(2) let f = (x, y) => x + y f(2) //│ JS: -//│ f6 = (...args) => { +//│ f4 = (...args) => { //│ globalThis.Predef.checkArgs("", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y; //│ }; -//│ f6(2) ?? null +//│ f4(2) ?? null //│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 -//│ f = [Function: f6] +//│ f = [Function: f4] :expect NaN @@ -100,7 +100,7 @@ Cls(1, 2).f(3) //│ tmp3 = tmp2 + z; //│ return tmp3 + p; //│ } -//│ toString() { return "Cls(" + this.x + ", " + this.y + ")"; } +//│ toString() { return "Cls(" + globalThis.Predef.render(this.x) + ", " + globalThis.Predef.render(this.y) + ")"; } //│ }; //│ tmp1 = Cls1(1, 2); //│ tmp1.f(3) ?? null @@ -115,7 +115,7 @@ Cls(1, 2).f(3) //│ Cls3 = function Cls(...args1) { //│ return new Cls.class(...args1); //│ }; -//│ Cls3.class = class Cls { +//│ Cls3.class = class Cls2 { //│ constructor(x, y) { //│ this.x = x; //│ this.y = y; @@ -129,7 +129,7 @@ Cls(1, 2).f(3) //│ tmp4 = tmp3 + z; //│ return tmp4 + p; //│ } -//│ toString() { return "Cls(" + this.x + ", " + this.y + ")"; } +//│ toString() { return "Cls(" + globalThis.Predef.render(this.x) + ", " + globalThis.Predef.render(this.y) + ")"; } //│ }; //│ tmp2 = Cls3(1, 2); //│ tmp2.f(3) ?? null @@ -145,7 +145,7 @@ Cls(1, 2).f(3, 4)(5) //│ Cls5 = function Cls(...args1) { //│ return new Cls.class(...args1); //│ }; -//│ Cls5.class = class Cls { +//│ Cls5.class = class Cls4 { //│ constructor(x, y) { //│ this.x = x; //│ this.y = y; @@ -166,7 +166,7 @@ Cls(1, 2).f(3, 4)(5) //│ return tmp8 + s; //│ }; //│ } -//│ toString() { return "Cls(" + this.x + ", " + this.y + ")"; } +//│ toString() { return "Cls(" + globalThis.Predef.render(this.x) + ", " + globalThis.Predef.render(this.y) + ")"; } //│ }; //│ tmp3 = Cls5(1, 2); //│ tmp4 = tmp3.f(3, 4); @@ -218,8 +218,8 @@ if M.A(1).y is x and x == 1 then x else 0 //│ JS: -//│ const M$class = class M { -//│ constructor() { +//│ M1 = class M { +//│ static { //│ this.A = function A(...args1) { //│ return new A.class(...args1); //│ }; @@ -232,12 +232,11 @@ if M.A(1).y is //│ let y = args[0]; //│ return this.x + y; //│ } -//│ toString() { return "A(" + this.x + ")"; } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ } -//│ toString() { return "M"; } -//│ }; M1 = new M$class; -//│ M1.class = M$class; +//│ static toString() { return "M"; } +//│ }; //│ tmp6 = M1.A(1); //│ selRes2 = tmp6.y; //│ if (selRes2 === undefined) { diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index e5ded9612..7858b1cd2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -38,7 +38,7 @@ set x += 1 in log(x) //│ try { //│ tmp3 = x1 + 1; //│ x1 = tmp3; -//│ tmp4 = log1(x1); +//│ tmp4 = log(x1); //│ tmp2 = tmp4; //│ } finally { //│ x1 = old; @@ -79,30 +79,30 @@ fun example() = log(get_x()) example() //│ JS (unsanitized): -//│ let example5; -//│ example5 = function example() { -//│ let x2, get$_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12; +//│ let example2; +//│ example2 = function example() { +//│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12; //│ x2 = 0; -//│ get$_x = () => { +//│ get_x = () => { //│ return x2; //│ }; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; //│ x2 = tmp6; -//│ tmp7 = log1(x2); -//│ tmp8 = (tmp7 , log1); -//│ tmp9 = get$_x() ?? null; +//│ tmp7 = log(x2); +//│ tmp8 = (tmp7 , log); +//│ tmp9 = get_x() ?? null; //│ tmp10 = tmp8(tmp9) ?? null; //│ tmp5 = tmp10; //│ } finally { //│ x2 = old1; //│ } -//│ tmp11 = log1(x2); -//│ tmp12 = get$_x() ?? null; -//│ return log1(tmp12); +//│ tmp11 = log(x2); +//│ tmp12 = get_x() ?? null; +//│ return log(tmp12); //│ }; -//│ example5() +//│ example2() //│ > 1 //│ > 1 //│ > 0 @@ -119,30 +119,30 @@ fun example() = y example() //│ JS (unsanitized): -//│ let example7; -//│ example7 = function example() { -//│ let x2, get$_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; +//│ let example3; +//│ example3 = function example() { +//│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; //│ x2 = 0; -//│ get$_x = () => { +//│ get_x = () => { //│ return x2; //│ }; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; //│ x2 = tmp6; -//│ tmp7 = log1(x2); +//│ tmp7 = log(x2); //│ tmp8 = (tmp7 , x2); //│ tmp5 = tmp8; //│ } finally { //│ x2 = old1; //│ } //│ y = tmp5; -//│ tmp9 = log1(x2); -//│ tmp10 = get$_x() ?? null; -//│ tmp11 = log1(tmp10); +//│ tmp9 = log(x2); +//│ tmp10 = get_x() ?? null; +//│ tmp11 = log(tmp10); //│ return y; //│ }; -//│ example7() +//│ example3() //│ > 1 //│ > 0 //│ > 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index d696e039c..e6db059b2 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -19,7 +19,7 @@ foo(0, ...a) :sjs foo(1, ...[2, 3], 4) //│ JS (unsanitized): -//│ foo1(1, ...[ 2, 3 ], 4) +//│ foo(1, ...[ 2, 3 ], 4) //│ = [ 1, 2, 3, 4 ] :re diff --git a/hkmc2/shared/src/test/mlscript/codegen/This.mls b/hkmc2/shared/src/test/mlscript/codegen/This.mls index e0fa37d01..4d2792051 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/This.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/This.mls @@ -8,13 +8,13 @@ fun test(x) = [this.a, x] //│ ═══[ERROR] Cannot use 'this' outside of an object scope. //│ JS (unsanitized): -//│ let test1; test1 = function test(x) { /* error */ }; null +//│ let test; test = function test(x) { /* error */ }; null :sjs fun test(x) = [globalThis.a, x] //│ JS (unsanitized): -//│ let test3; test3 = function test(x) { return [ globalThis.a, x ]; }; null +//│ let test1; test1 = function test(x) { return [ globalThis.a, x ]; }; null :re test(123) @@ -41,22 +41,21 @@ module Test with [this.a, x] //│ JS (unsanitized): //│ let Test1; -//│ const Test$class = class Test { -//│ constructor() { +//│ Test1 = class Test { +//│ static { //│ this.a = 3; //│ } -//│ test1(x) { -//│ return test3(x); +//│ static test1(x) { +//│ return test1(x); //│ } -//│ test2(x1) { +//│ static test2(x1) { //│ return [ -//│ this.a, +//│ Test.a, //│ x1 //│ ]; //│ } -//│ toString() { return "Test"; } -//│ }; Test1 = new Test$class; -//│ Test1.class = Test$class; +//│ static toString() { return "Test"; } +//│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls b/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls index 41425e4c7..6382b3e91 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls @@ -43,7 +43,7 @@ Example . oops(2) //│ rhs = Tup of Ls of //│ IntLit of 2 //│ JS (unsanitized): -//│ let tmp1; tmp1 = call1(Example1, oops); tmp1(2) ?? null +//│ let tmp1; tmp1 = call(Example1, oops); tmp1(2) ?? null //│ = [ 2, 1 ] Example. oops(2) @@ -108,7 +108,7 @@ Example .> oops(2) //│ rhs = Tup of Ls of //│ IntLit of 2 //│ JS (unsanitized): -//│ let tmp25; tmp25 = oops(2) ?? null; call3(Example1, tmp25) +//│ let tmp25; tmp25 = oops(2) ?? null; call1(Example1, tmp25) //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'a') // * Note: diff --git a/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls b/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls index be67cd337..dd2b6cd89 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ThisCalls.mls @@ -19,7 +19,39 @@ s(123) :sjs ex |>. s(123) //│ JS (unsanitized): -//│ let tmp2; tmp2 = Predef.call(ex1, s); tmp2(123) ?? null +//│ let tmp2; tmp2 = Predef.call(ex, s); tmp2(123) ?? null //│ = [ 123, 456 ] +// * Modules on the other hand, do not suffer from method debinding, +// * because they use static references and avoid `this` altogether. + +module Example with + val a = 456 + fun f(x) = [x, a] + fun g(x) = [x, a, f(x)] + +let + f = Example.f + g = Example.g +//│ f = [Function: f] +//│ g = [Function: g] + +f(123) +//│ = [ 123, 456 ] + +g(123) +//│ = [ 123, 456, [ 123, 456 ] ] + +:e +Example |>. g(123) +//│ ╔══[ERROR] Only module parameters may receive module arguments (values). +//│ ║ l.47: Example |>. g(123) +//│ ╙── ^^^^^^^ +//│ = [ 123, 456, [ 123, 456 ] ] + +// * Passing `ex` as `this` has no effect +ex |>. g(123) +//│ = [ 123, 456, [ 123, 456 ] ] + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls index afa35888d..7c3e96531 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls @@ -31,7 +31,7 @@ fun f(x) = return y f(1) //│ JS (unsanitized): -//│ let f5; f5 = function f(x) { throw globalThis.Error("e"); }; f5(1) +//│ let f2; f2 = function f(x) { throw globalThis.Error("e"); }; f2(1) //│ ═══[RUNTIME ERROR] Error: e @@ -41,13 +41,13 @@ fun f(x) = throw (if x then Error("x") else Error("y")) f(false) //│ JS (unsanitized): -//│ let f7; -//│ f7 = function f(x) { +//│ let f3; +//│ f3 = function f(x) { //│ if (x === true) { //│ throw globalThis.Error("x"); //│ } else { //│ throw globalThis.Error("y"); //│ } //│ }; -//│ f7(false) +//│ f3(false) //│ ═══[RUNTIME ERROR] Error: y diff --git a/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls b/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls index 1af9de791..bd6247b28 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TraceLog.mls @@ -7,17 +7,17 @@ fun fib(a) = if a <= 1 then a else fib(a - 1) + fib(a - 2) //│ JS (unsanitized): -//│ let fib1; -//│ fib1 = function fib(a) { +//│ let fib; +//│ fib = function fib(a) { //│ let scrut, tmp, tmp1, tmp2, tmp3; //│ scrut = a <= 1; //│ if (scrut === true) { //│ return a; //│ } else { //│ tmp = a - 1; -//│ tmp1 = fib1(tmp); +//│ tmp1 = fib(tmp); //│ tmp2 = a - 2; -//│ tmp3 = fib1(tmp2); +//│ tmp3 = fib(tmp2); //│ return tmp1 + tmp3; //│ } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls index e5decaca3..38c1643c6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls @@ -60,7 +60,7 @@ f(1,2)(-3) //│ REPL> Sending: globalThis.Predef.TraceLogger.enabled = true; globalThis.Predef.TraceLogger.resetIndent(0) //│ REPL> Collected: //│ > null -//│ REPL> Sending: let tmp4, tmp5;try { tmp4 = f1(1, 2); tmp5 = - 3; tmp4(tmp5) ?? null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let tmp4, tmp5;try { tmp4 = f(1, 2); tmp5 = - 3; tmp4(tmp5) ?? null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > CALL g(-3) //│ > | CALL g(-2) @@ -90,7 +90,7 @@ f(1,2)(-3) :showRepl f(1,2)(-4) -//│ REPL> Sending: let tmp6, tmp7;try { tmp6 = f1(1, 2); tmp7 = - 4; tmp6(tmp7) ?? null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let tmp6, tmp7;try { tmp6 = f(1, 2); tmp7 = - 4; tmp6(tmp7) ?? null } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > 45 //│ REPL> Parsed: diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index 80ae69427..3d62b0e93 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -57,7 +57,7 @@ while x //│ let tmp4, tmp5; //│ tmp6: while (true) { //│ if (x2 === true) { -//│ tmp4 = log1("Hello World"); +//│ tmp4 = log("Hello World"); //│ x2 = false; //│ tmp5 = null; //│ continue tmp6; @@ -198,8 +198,8 @@ fun f(ls) = log(h) else log("Done!") //│ JS (unsanitized): -//│ let f1; -//│ f1 = function f(ls) { +//│ let f; +//│ f = function f(ls) { //│ let param0, param1, h, tl, tmp28; //│ tmp29: while (true) { //│ if (ls instanceof Cons1.class) { @@ -208,10 +208,10 @@ fun f(ls) = //│ h = param0; //│ tl = param1; //│ ls = tl; -//│ tmp28 = log1(h); +//│ tmp28 = log(h); //│ continue tmp29; //│ } else { -//│ tmp28 = log1("Done!"); +//│ tmp28 = log("Done!"); //│ } //│ break; //│ } diff --git a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls index cf0c81bd7..6e9616c2e 100644 --- a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls +++ b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls @@ -6,6 +6,8 @@ declare type Nothing declare type untyped declare class Object +declare class Array +declare class JSON declare class Bool declare class Int declare class Num diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index c20094cde..f2b1cb688 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -156,13 +156,13 @@ if true do fun f() = 3 f() //│ JS (unsanitized): -//│ let tmp11, handleBlock$23; -//│ handleBlock$23 = function handleBlock$() { -//│ let h, scrut, f, tmp12, res, Cont$, Handler$h$; -//│ Handler$h$ = function Handler$h$() { +//│ let tmp11, handleBlock$11; +//│ handleBlock$11 = function handleBlock$() { +//│ let h, scrut, f, tmp12, res, Cont$22, Handler$h$12; +//│ Handler$h$12 = function Handler$h$() { //│ return new Handler$h$.class(); //│ }; -//│ Handler$h$.class = class Handler$h$ extends Effect1 { +//│ Handler$h$12.class = class Handler$h$11 extends Effect1 { //│ constructor() { //│ let tmp13; //│ tmp13 = super(); @@ -172,13 +172,13 @@ if true do //│ return arg; //│ }); //│ } -//│ toString() { return "Handler$h$(" + + ")"; } +//│ toString() { return "Handler$h$(" + "" + ")"; } //│ }; -//│ h = Handler$h$(); -//│ Cont$ = function Cont$(pc1) { +//│ h = Handler$h$12(); +//│ Cont$22 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$22.class = class Cont$21 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp13; //│ tmp13 = super(null, null); @@ -199,7 +199,7 @@ if true do //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ f = function f() { //│ return 3; @@ -208,7 +208,7 @@ if true do //│ if (scrut === true) { //│ res = f(); //│ if (res instanceof globalThis.Predef.__EffectSig.class) { -//│ res.tail.next = Cont$(0); +//│ res.tail.next = Cont$22(0); //│ return globalThis.Predef.__handleBlockImpl(res, h); //│ } //│ tmp12 = res; @@ -217,7 +217,7 @@ if true do //│ } //│ return tmp12; //│ }; -//│ tmp11 = handleBlock$23(); +//│ tmp11 = handleBlock$11(); //│ if (tmp11 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls index 71a19c742..cb358422a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsHygiene.mls @@ -12,24 +12,22 @@ fun foo(h) = module A A //│ JS (unsanitized): -//│ let foo1; -//│ foo1 = function foo(h) { -//│ let A, scrut, A1; -//│ const A$class = class A { -//│ constructor() {} -//│ toString() { return "A"; } -//│ }; A1 = new A$class; -//│ A1.class = A$class; -//│ const A$class1 = class A { -//│ constructor() {} -//│ toString() { return "A"; } -//│ }; A = new A$class1; -//│ A.class = A$class1; +//│ let foo; +//│ foo = function foo(h) { +//│ let A2, scrut, A3; +//│ A3 = class A { +//│ static {} +//│ static toString() { return "A"; } +//│ }; +//│ A2 = class A1 { +//│ static {} +//│ static toString() { return "A"; } +//│ }; //│ scrut = false; //│ if (scrut === true) { -//│ return A1; +//│ return A3; //│ } else { -//│ return A; +//│ return A2; //│ } //│ }; //│ null diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index 241a93549..aa36ec21b 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -17,12 +17,12 @@ class Lol(h) with //│ Lol1.class = class Lol { //│ constructor(h) { //│ this.h = h; -//│ let tmp, res, res1, Cont$; +//│ let tmp, res, res1, Cont$1; //│ const this$Lol = this; -//│ Cont$ = function Cont$(pc1) { +//│ Cont$1 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp1; //│ tmp1 = super(null, null); @@ -53,24 +53,24 @@ class Lol(h) with //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ res = this.h.perform("k") ?? null; //│ if (res instanceof globalThis.Predef.__EffectSig.class) { -//│ res.tail.next = Cont$(0); +//│ res.tail.next = Cont$1(0); //│ res.tail = res.tail.next; //│ return res; //│ } //│ tmp = res; //│ res1 = Predef.print(tmp); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$(1); +//│ res1.tail.next = Cont$1(1); //│ res1.tail = res1.tail.next; //│ return res1; //│ } //│ res1 //│ } -//│ toString() { return "Lol(" + this.h + ")"; } +//│ toString() { return "Lol(" + globalThis.Predef.render(this.h) + ")"; } //│ }; //│ null @@ -110,9 +110,9 @@ let oops = Lol(h) //│ > k //│ > b -//│ oops = Lol { h: Handler$h$ {} } +//│ oops = Lol { h: Handler$h$2 {} } oops.h -//│ = Handler$h$ {} +//│ = Handler$h$2 {} diff --git a/hkmc2/shared/src/test/mlscript/handlers/Generators.mls b/hkmc2/shared/src/test/mlscript/handlers/Generators.mls index 7ca86ccdf..6789d131a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Generators.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Generators.mls @@ -48,12 +48,12 @@ fun permutations_foreach(l, f) = resume(()) permutations(gen, l) permutations_foreach([1, 2, 3], print) -//│ > 1,2,3 -//│ > 1,3,2 -//│ > 2,1,3 -//│ > 2,3,1 -//│ > 3,1,2 -//│ > 3,2,1 +//│ > [1, 2, 3] +//│ > [1, 3, 2] +//│ > [2, 1, 3] +//│ > [2, 3, 1] +//│ > [3, 1, 2] +//│ > [3, 2, 1] fun permutations_impl(gen, l1, l2) = diff --git a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls index b041b973d..2fd08e337 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/HandlersInClasses.mls @@ -32,7 +32,7 @@ class Lol() with // FIXME: should NOT return a partially initialized object let oops = Lol() //│ > k -//│ oops = Lol {} +//│ oops = Lol2 {} :re oops.x diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 6e7412f62..218ace4f0 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -129,27 +129,27 @@ if true do h1.perform(()) str //│ JS (unsanitized): -//│ let str, scrut, tmp8, tmp9, handleBlock$9; +//│ let str, scrut, tmp8, tmp9, handleBlock$4; //│ str = ""; //│ scrut = true; //│ if (scrut === true) { -//│ handleBlock$9 = function handleBlock$() { -//│ let h1, tmp10, handleBlock$10, Cont$, Handler$h1$; -//│ Handler$h1$ = function Handler$h1$() { +//│ handleBlock$4 = function handleBlock$() { +//│ let h1, tmp10, handleBlock$5, Cont$16, Handler$h1$2; +//│ Handler$h1$2 = function Handler$h1$() { //│ return new Handler$h1$.class(); //│ }; -//│ Handler$h1$.class = class Handler$h1$ extends Effect1 { +//│ Handler$h1$2.class = class Handler$h1$1 extends Effect1 { //│ constructor() { //│ let tmp11; //│ tmp11 = super(); //│ } //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(this, (k) => { -//│ let tmp11, tmp12, tmp13, res8, Cont$1; -//│ Cont$1 = function Cont$(pc1) { +//│ let tmp11, tmp12, tmp13, res8, Cont$17; +//│ Cont$17 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$17.class = class Cont$12 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp14; //│ tmp14 = super(null, null); @@ -169,13 +169,13 @@ str //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ tmp11 = str + "A"; //│ str = tmp11; //│ res8 = k(arg) ?? null; //│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ res8.tail.next = Cont$1(5); +//│ res8.tail.next = Cont$17(5); //│ res8.tail = res8.tail.next; //│ return res8; //│ } @@ -185,13 +185,13 @@ str //│ return null; //│ }); //│ } -//│ toString() { return "Handler$h1$(" + + ")"; } +//│ toString() { return "Handler$h1$(" + "" + ")"; } //│ }; -//│ h1 = Handler$h1$(); -//│ Cont$ = function Cont$(pc1) { +//│ h1 = Handler$h1$2(); +//│ Cont$16 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$16.class = class Cont$13 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp11; //│ tmp11 = super(null, null); @@ -214,25 +214,25 @@ str //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ handleBlock$10 = function handleBlock$() { -//│ let h2, tmp11, res8, res9, Cont$1, Handler$h2$; -//│ Handler$h2$ = function Handler$h2$() { +//│ handleBlock$5 = function handleBlock$() { +//│ let h2, tmp11, res8, res9, Cont$17, Handler$h2$2; +//│ Handler$h2$2 = function Handler$h2$() { //│ return new Handler$h2$.class(); //│ }; -//│ Handler$h2$.class = class Handler$h2$ extends Effect1 { +//│ Handler$h2$2.class = class Handler$h2$1 extends Effect1 { //│ constructor() { //│ let tmp12; //│ tmp12 = super(); //│ } //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(this, (k) => { -//│ let tmp12, tmp13, tmp14, tmp15, tmp16, res10, Cont$2; -//│ Cont$2 = function Cont$(pc1) { +//│ let tmp12, tmp13, tmp14, tmp15, tmp16, res10, Cont$18; +//│ Cont$18 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$2.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$18.class = class Cont$14 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp17; //│ tmp17 = super(null, null); @@ -253,14 +253,14 @@ str //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ tmp12 = str + "B"; //│ tmp13 = str + tmp12; //│ str = tmp13; //│ res10 = k(arg) ?? null; //│ if (res10 instanceof globalThis.Predef.__EffectSig.class) { -//│ res10.tail.next = Cont$2(2); +//│ res10.tail.next = Cont$18(2); //│ res10.tail = res10.tail.next; //│ return res10; //│ } @@ -271,13 +271,13 @@ str //│ return null; //│ }); //│ } -//│ toString() { return "Handler$h2$(" + + ")"; } +//│ toString() { return "Handler$h2$(" + "" + ")"; } //│ }; -//│ h2 = Handler$h2$(); -//│ Cont$1 = function Cont$(pc1) { +//│ h2 = Handler$h2$2(); +//│ Cont$17 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$17.class = class Cont$15 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp12; //│ tmp12 = super(null, null); @@ -305,24 +305,24 @@ str //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ res8 = h2.perform(null) ?? null; //│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ res8.tail.next = Cont$1(0); +//│ res8.tail.next = Cont$17(0); //│ return globalThis.Predef.__handleBlockImpl(res8, h2); //│ } //│ tmp11 = res8; //│ res9 = h1.perform(null) ?? null; //│ if (res9 instanceof globalThis.Predef.__EffectSig.class) { -//│ res9.tail.next = Cont$1(1); +//│ res9.tail.next = Cont$17(1); //│ return globalThis.Predef.__handleBlockImpl(res9, h2); //│ } //│ return res9; //│ }; -//│ tmp10 = handleBlock$10(); +//│ tmp10 = handleBlock$5(); //│ if (tmp10 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp10.tail.next = Cont$(3); +//│ tmp10.tail.next = Cont$16(3); //│ return globalThis.Predef.__handleBlockImpl(tmp10, h1); //│ } //│ if (tmp10 instanceof globalThis.Predef.__Return.class) { @@ -330,7 +330,7 @@ str //│ } //│ return tmp10; //│ }; -//│ tmp8 = handleBlock$9(); +//│ tmp8 = handleBlock$4(); //│ if (tmp8 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 676fbf509..520adcbd7 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -20,13 +20,13 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi1, res, handleBlock$1; -//│ hi1 = function hi(n) { -//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, res1, Cont$; -//│ Cont$ = function Cont$(pc1) { +//│ let hi, res, handleBlock$; +//│ hi = function hi(n) { +//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, res1, Cont$3; +//│ Cont$3 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp1; //│ tmp1 = super(null, null); @@ -44,7 +44,7 @@ hi(0) //│ } else { //│ tmp = n - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi1(tmp); +//│ return hi(tmp); //│ } //│ this.pc = 1; //│ continue contLoop; @@ -58,7 +58,7 @@ hi(0) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; @@ -67,7 +67,7 @@ hi(0) //│ if (scrut1 === true) { //│ res1 = globalThis.Predef.__stackHandler.perform(); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$(0); +//│ res1.tail.next = Cont$3(0); //│ res1.tail = res1.tail.next; //│ return res1; //│ } @@ -79,26 +79,26 @@ hi(0) //│ } else { //│ tmp = n - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi1(tmp); +//│ return hi(tmp); //│ } //│ }; -//│ handleBlock$1 = function handleBlock$() { -//│ let stackHandler, res1, Cont$, StackDelay$; -//│ StackDelay$ = function StackDelay$() { +//│ handleBlock$ = function handleBlock$() { +//│ let stackHandler, res1, Cont$3, StackDelay$1; +//│ StackDelay$1 = function StackDelay$() { //│ return new StackDelay$.class(); //│ }; -//│ StackDelay$.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { //│ constructor() { //│ let tmp; //│ tmp = super(); //│ } //│ perform() { //│ return globalThis.Predef.__mkEffect(this, (resume) => { -//│ let res2, res3, Cont$1; -//│ Cont$1 = function Cont$(pc1) { +//│ let res2, res3, Cont$4; +//│ Cont$4 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); @@ -116,12 +116,12 @@ hi(0) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res3 = resume(); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$1(4); +//│ res3.tail.next = Cont$4(4); //│ res3.tail = res3.tail.next; //│ return res3; //│ } @@ -129,13 +129,13 @@ hi(0) //│ return res2; //│ }); //│ } -//│ toString() { return "StackDelay$(" + + ")"; } +//│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ stackHandler = StackDelay$(); -//│ Cont$ = function Cont$(pc1) { +//│ stackHandler = StackDelay$1(); +//│ Cont$3 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$3.class = class Cont$2 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); @@ -152,20 +152,20 @@ hi(0) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ globalThis.Predef.__stackLimit = 5; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ res1 = hi1(0); +//│ res1 = hi(0); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$(3); +//│ res1.tail.next = Cont$3(3); //│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler); //│ } //│ return res1; //│ }; -//│ res = handleBlock$1(); +//│ res = handleBlock$(); //│ if (res instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } @@ -184,13 +184,13 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum3, res1, handleBlock$3; -//│ sum3 = function sum(n) { -//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$; -//│ Cont$ = function Cont$(pc1) { +//│ let sum1, res1, handleBlock$1; +//│ sum1 = function sum(n) { +//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$6; +//│ Cont$6 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$6.class = class Cont$3 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp3; //│ tmp3 = super(null, null); @@ -211,7 +211,7 @@ sum(10000) //│ tmp = n - 1; //│ prevDepth = globalThis.Predef.__stackDepth; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ res3 = sum3(tmp); +//│ res3 = sum1(tmp); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; //│ return globalThis.Predef.__appendInCont(res3, this); @@ -245,7 +245,7 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; @@ -254,7 +254,7 @@ sum(10000) //│ if (scrut1 === true) { //│ res2 = globalThis.Predef.__stackHandler.perform(); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$(0); +//│ res2.tail.next = Cont$6(0); //│ res2.tail = res2.tail.next; //│ return res2; //│ } @@ -267,9 +267,9 @@ sum(10000) //│ tmp = n - 1; //│ prevDepth = globalThis.Predef.__stackDepth; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ res3 = sum3(tmp); +//│ res3 = sum1(tmp); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$(1); +//│ res3.tail.next = Cont$6(1); //│ res3.tail = res3.tail.next; //│ return res3; //│ } @@ -283,23 +283,23 @@ sum(10000) //│ return n + tmp1; //│ } //│ }; -//│ handleBlock$3 = function handleBlock$() { -//│ let stackHandler, res2, Cont$, StackDelay$; -//│ StackDelay$ = function StackDelay$() { +//│ handleBlock$1 = function handleBlock$() { +//│ let stackHandler, res2, Cont$6, StackDelay$2; +//│ StackDelay$2 = function StackDelay$() { //│ return new StackDelay$.class(); //│ }; -//│ StackDelay$.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ StackDelay$2.class = class StackDelay$1 extends globalThis.Predef.__StackDelay.class { //│ constructor() { //│ let tmp; //│ tmp = super(); //│ } //│ perform() { //│ return globalThis.Predef.__mkEffect(this, (resume) => { -//│ let res3, res4, Cont$1; -//│ Cont$1 = function Cont$(pc1) { +//│ let res3, res4, Cont$7; +//│ Cont$7 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$1.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$7.class = class Cont$4 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); @@ -317,12 +317,12 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res4 = resume(); //│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = Cont$1(6); +//│ res4.tail.next = Cont$7(6); //│ res4.tail = res4.tail.next; //│ return res4; //│ } @@ -330,13 +330,13 @@ sum(10000) //│ return res3; //│ }); //│ } -//│ toString() { return "StackDelay$(" + + ")"; } +//│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ stackHandler = StackDelay$(); -//│ Cont$ = function Cont$(pc1) { +//│ stackHandler = StackDelay$2(); +//│ Cont$6 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$6.class = class Cont$5 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); @@ -353,20 +353,20 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + this.pc + ")"; } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ globalThis.Predef.__stackLimit = 1000; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ res2 = sum3(10000); +//│ res2 = sum1(10000); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$(5); +//│ res2.tail.next = Cont$6(5); //│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler); //│ } //│ return res2; //│ }; -//│ res1 = handleBlock$3(); +//│ res1 = handleBlock$1(); //│ if (res1 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } @@ -467,8 +467,8 @@ foo(h) :sjs fun max(a, b) = if a < b then b else a //│ JS (unsanitized): -//│ let max1; -//│ max1 = function max(a, b) { +//│ let max; +//│ max = function max(a, b) { //│ let scrut; //│ scrut = a < b; //│ if (scrut === true) { @@ -488,11 +488,12 @@ fun hi(n) = n hi(0) //│ ═══[COMPILATION ERROR] This code requires effect handler instrumentation but was compiled without it. //│ JS (unsanitized): -//│ let hi3, stackHandler; -//│ hi3 = function hi(n) { +//│ let hi1, stackHandler; +//│ hi1 = function hi(n) { //│ return n; //│ }; //│ throw globalThis.Error("This code requires effect handler instrumentation but was compiled without it."); +//│ ═══[RUNTIME ERROR] Error: This code requires effect handler instrumentation but was compiled without it. :stackSafe 42 :ge diff --git a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls index 12244aa8e..d86af1ff8 100644 --- a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls +++ b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls @@ -8,7 +8,7 @@ :sjs hello() //│ JS (unsanitized): -//│ hello1() +//│ hello() //│ = 'Hello!' @@ -17,6 +17,6 @@ fun hello() = "Hello?" :sjs hello() //│ JS (unsanitized): -//│ hello3() +//│ hello1() //│ = 'Hello?' diff --git a/hkmc2/shared/src/test/mlscript/rp/LocalPatterns.mls b/hkmc2/shared/src/test/mlscript/rp/LocalPatterns.mls index 93bff10a1..be5c45848 100644 --- a/hkmc2/shared/src/test/mlscript/rp/LocalPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/rp/LocalPatterns.mls @@ -8,11 +8,10 @@ module Playground with pattern ZeroOne = Zero ~ One Playground -//│ > Playground { +//│ > [class Playground] { //│ > Zero: Zero { class: [class Zero] }, //│ > DoubleZero: DoubleZero { class: [class DoubleZero] }, -//│ > ZeroOne: ZeroOne { class: [class ZeroOne] }, -//│ > class: [class Playground] +//│ > ZeroOne: ZeroOne { class: [class ZeroOne] } //│ = } // Pattern defined in a module can be used with qualified name. diff --git a/hkmc2/shared/src/test/mlscript/rp/MatchResult.mls b/hkmc2/shared/src/test/mlscript/rp/MatchResult.mls index c506e4630..1c8e9ab72 100644 --- a/hkmc2/shared/src/test/mlscript/rp/MatchResult.mls +++ b/hkmc2/shared/src/test/mlscript/rp/MatchResult.mls @@ -28,8 +28,8 @@ Cross.unapply("0") :sjs fun foo(x) = x is Cross //│ JS (unsanitized): -//│ let foo1; -//│ foo1 = function foo(x2) { +//│ let foo; +//│ foo = function foo(x2) { //│ let matchResult; //│ matchResult = Cross1.unapply(x2) ?? null; //│ if (matchResult instanceof globalThis.Predef.MatchResult.class) { diff --git a/hkmc2/shared/src/test/mlscript/basics/PredefTest.mls b/hkmc2/shared/src/test/mlscript/std/PredefTest.mls similarity index 93% rename from hkmc2/shared/src/test/mlscript/basics/PredefTest.mls rename to hkmc2/shared/src/test/mlscript/std/PredefTest.mls index df9b82c44..ff5518437 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PredefTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/PredefTest.mls @@ -44,6 +44,14 @@ print(1, 2, 3) 1 . print() //│ > 1 +1 . (print(_))() +//│ > 1 + +:e +1 . print(_)() +//│ ═══[ERROR] Illegal position for '_' placeholder. +//│ = [Function (anonymous)] + // FIXME: should have the same semantics... 1 . print() diff --git a/hkmc2/shared/src/test/mlscript/std/Rendering.mls b/hkmc2/shared/src/test/mlscript/std/Rendering.mls new file mode 100644 index 000000000..c43a24b6f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/std/Rendering.mls @@ -0,0 +1,78 @@ +:js + + +render(123) +//│ = '123' + +render("123") +//│ = '"123"' + +render("12\n3") +//│ = '"12\\n3"' + +render("12\\n3") +//│ = '"12\\\\n3"' + +render([]) +//│ = '[]' + +render([1]) +//│ = '[1]' + +render([1, 2]) +//│ = '[1, 2]' + +render([1,2,3]) +//│ = '[1, 2, 3]' + +render([1,2,3,4]) +//│ = '[1, 2, 3, 4]' + +render(1) +//│ = '1' + + +interleave(0) of 1, 2, 3 +//│ = [ 1, 0, 2, 0, 3 ] + +let arg = [1, 2] +//│ arg = [ 1, 2 ] + +map(render)(...arg) +//│ = [ '1', '2' ] + +fold(+)("[", ...interleave("|")(...map(render)(...arg)), "]") +//│ = '[1|2]' + +interleave(", ") of 1, 2 +//│ = [ 1, ', ', 2 ] + + +class Foo(val xs) + +render(Foo([1, 2, 3])) +//│ = 'Foo([1, 2, 3])' + +render([Foo([1]), Foo([2])]) +//│ = '[Foo([1]), Foo([2])]' + + +renderAsStr("1") +//│ = '1' + +renderAsStr(1) +//│ = '1' + +let xs = [1, 2] +//│ xs = [ 1, 2 ] + +map(renderAsStr)(xs) +//│ = [ '[1, 2]' ] + +map(renderAsStr(_))(xs) +//│ = [ '[1, 2]' ] + +map(_ + 1)(...xs) +//│ = [ 2, 3 ] + + diff --git a/hkmc2/shared/src/test/mlscript/std/StackTests.mls b/hkmc2/shared/src/test/mlscript/std/StackTests.mls index 224fedd11..fdc3338cc 100644 --- a/hkmc2/shared/src/test/mlscript/std/StackTests.mls +++ b/hkmc2/shared/src/test/mlscript/std/StackTests.mls @@ -57,6 +57,6 @@ s1 zip(s2) print of ... s1 zip of s2, s3 -//│ > Cons(1,a,true, Cons(2,b,false, Cons(3,c, Nil))) +//│ > Cons([1, "a", true], Cons([2, "b", false], Cons([3, "c"], Nil))) diff --git a/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls index 5632be018..f1bd25758 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/KeywordStutters.mls @@ -52,8 +52,8 @@ module \$ extends Foo //│ ╔══[PARSE ERROR] Expected end of input; found error instead //│ ║ l.46: module \$ extends Foo //│ ╙── ^ -//│ > let $_1;try { const $_$class = class \ { constructor() {} toString() { return "\"; } }; $_1 = new $_$class; $_1.class = $_$class; null } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^ +//│ > let $_1;try { $_1 = class $_ { static {} static toString() { return "\"; } }; null } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Invalid or unexpected token diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls index c37e716c8..d7332a4db 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls @@ -3,7 +3,7 @@ module tailrec tailrec -//│ = tailrec { class: [class tailrec] } +//│ = [class tailrec] :w @tailrec fun fact_n(n, acc) = diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls index 6069bc317..d0bee2c13 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -36,8 +36,8 @@ fun f(x) = if x is Pair(A, A) then 1 Pair(B, B) then 2 //│ JS (unsanitized): -//│ let f1; -//│ f1 = function f(x) { +//│ let f; +//│ f = function f(x) { //│ let param0, param1; //│ if (x instanceof Pair1.class) { //│ param0 = x.a; diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls index bffbea9ad..801ad9baf 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls @@ -17,8 +17,8 @@ fun nonsense(xs) = if xs is [..ys] then ys [] then "empty" //│ JS (unsanitized): -//│ let nonsense1; -//│ nonsense1 = function nonsense(xs) { +//│ let nonsense; +//│ nonsense = function nonsense(xs) { //│ let rest, ys; //│ if (globalThis.Array.isArray(xs) && xs.length >= 0) { //│ rest = globalThis.Predef.tupleSlice(xs, 0, 0) ?? null; @@ -41,8 +41,8 @@ fun lead_and_last(xs) = if xs is [x, ..ys, y] then x + y [] then 0 //│ JS (unsanitized): -//│ let lead$_and$_last1; -//│ lead$_and$_last1 = function lead_and_last(xs) { +//│ let lead_and_last; +//│ lead_and_last = function lead_and_last(xs) { //│ let last0, rest, first0, x, ys, y; //│ if (globalThis.Array.isArray(xs) && xs.length >= 2) { //│ first0 = xs[0]; @@ -80,8 +80,8 @@ fun nested_tuple_patterns(xs) = if xs is [x, ..[y, z], w] then x + y + z + w [] then 0 //│ JS (unsanitized): -//│ let nested$_tuple$_patterns1; -//│ nested$_tuple$_patterns1 = function nested_tuple_patterns(xs) { +//│ let nested_tuple_patterns; +//│ nested_tuple_patterns = function nested_tuple_patterns(xs) { //│ let last0, rest, first0, x, first1, first01, y, z, w, tmp2, tmp3; //│ if (globalThis.Array.isArray(xs) && xs.length >= 2) { //│ first0 = xs[0]; diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala index 40beb9b13..215c2dc75 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/DiffMaker.scala @@ -106,6 +106,7 @@ abstract class DiffMaker: val fullExceptionStack = NullaryCommand("s") + val verbose = NullaryCommand("v") val debug = NullaryCommand("d") val expectParseErrors = NullaryCommand("pe") diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 5c53df047..e8e8de206 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -57,7 +57,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: override def processTerm(blk: semantics.Term.Blk, inImport: Bool)(using Raise): Unit = super.processTerm(blk, inImport) val outerRaise: Raise = summon - var showingJSYieldedCompileError = false + val reportedMessages = mutable.Set.empty[Str] val stackLimit = stackSafe.get match case None => None case Some("off") => None @@ -73,7 +73,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: if showJS.isSet then given Raise = case d @ ErrorReport(source = Source.Compilation) => - showingJSYieldedCompileError = true + reportedMessages += d.mainMsg outerRaise(d) case d => outerRaise(d) given Elaborator.Ctx = curCtx @@ -81,8 +81,9 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit, lift = lift.isSet) with codegen.LoweringSelSanityChecks(instrument = false) with codegen.LoweringTraceLog(instrument = false) - val jsb = new JSBuilder - with JSBuilderArgNumSanityChecks(instrument = false) + val jsb = ltl.givenIn: + new JSBuilder + with JSBuilderArgNumSanityChecks(instrument = false) val le = low.program(blk) val nestedScp = baseScp.nest val je = nestedScp.givenIn: @@ -90,14 +91,20 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: val jsStr = je.stripBreaks.mkString(100) output(s"JS (unsanitized):") output(jsStr) - if js.isSet && !showingJSYieldedCompileError then + if js.isSet then given Elaborator.Ctx = curCtx + given Raise = + case e: ErrorReport if reportedMessages.contains(e.mainMsg) => + if verbose.isSet then + output(s"Skipping already reported diagnostic: ${e.mainMsg}") + case d => outerRaise(d) val low = ltl.givenIn: new codegen.Lowering(lowerHandlers = handler.isSet, stackLimit = stackLimit, lift = lift.isSet) with codegen.LoweringSelSanityChecks(noSanityCheck.isUnset) with codegen.LoweringTraceLog(traceJS.isSet) - val jsb = new JSBuilder - with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) + val jsb = ltl.givenIn: + new JSBuilder + with JSBuilderArgNumSanityChecks(noSanityCheck.isUnset) val le = low.program(blk) if showLoweredTree.isSet then output(s"Lowered:") From bce89f932c34d80d1cc586bccfd1aeccbdb92b27 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 31 Jan 2025 16:49:20 +0800 Subject: [PATCH 033/127] Update blocktransformer and other code to support dynamic fields --- .../hkmc2/codegen/BlockTransformer.scala | 29 +++++++++++++++---- .../scala/hkmc2/codegen/HandlerLowering.scala | 3 ++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 1f7025e75..928083f96 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -83,6 +83,13 @@ class BlockTransformer(subst: SymbolSubst): if (l2 is l) && (res2 is res) && (par2 is par) && (cls2 is cls) && (hdr2 is hdr) && (bod2 is bod) && (rst2 is rst) then b else HandleBlock(l2, res2, par2, cls2, hdr2, bod2, rst2) + case AssignDynField(l, fld, arrayIdx, r, rst) => + applyResult2(r): r2 => + val l2 = applyPath(l) + val fld2 = applyPath(fld) + val rst2 = applyBlock(rst) + if (l2 is l) && (fld2 is fld) && (r2 is r) && (rst2 is rst) + then b else AssignDynField(l2, fld2, arrayIdx, r2, rst2) def applyResult2(r: Result)(k: Result => Block): Block = k(applyResult(r)) @@ -98,6 +105,10 @@ class BlockTransformer(subst: SymbolSubst): case p: Path => applyPath(p) def applyPath(p: Path): Path = p match + case DynSelect(qual, fld, arrayIdx) => + val qual2 = applyPath(qual) + val fld2 = applyPath(fld) + if (qual2 is qual) && (fld2 is fld) then p else DynSelect(qual2, fld2, arrayIdx) case p @ Select(qual, name) => val qual2 = applyPath(qual) val sym2 = p.symbol.mapConserve(_.subst) @@ -232,12 +243,18 @@ class BlockTransformerNoRec(subst: SymbolSubst) extends BlockTransformerShallow( val scrut2 = applyPath(scrut) if (scrut is scrut2) then b else Match(scrut2, arms, dflt, rest) case Assign(lhs, rhs, rest) => - val lhs2 = lhs.subst - val rhs2 = applyResult(rhs) - if (lhs is lhs2) && (rhs is rhs2) then b else Assign(lhs2, rhs2, rest) + applyResult2(rhs): rhs2 => + val lhs2 = lhs.subst + if (lhs is lhs2) && (rhs is rhs2) then b else Assign(lhs2, rhs2, rest) case AssignField(lhs, ident, rhs, rest) => - val lhs2 = applyPath(lhs) - val rhs2 = applyResult(rhs) - if rhs is rhs2 then b else AssignField(lhs2, ident, rhs2, rest)(N) + applyResult2(rhs): rhs2 => + val lhs2 = applyPath(lhs) + if rhs is rhs2 then b else AssignField(lhs2, ident, rhs2, rest)(N) + case AssignDynField(l, fld, arrayIdx, r, rst) => + applyResult2(r): r2 => + val l2 = applyPath(l) + val fld2 = applyPath(fld) + if (l2 is l) && (fld2 is fld) && (r2 is r) + then b else AssignDynField(l2, fld2, arrayIdx, r2, rst) case _: Label | _: Begin | _: TryBlock | _: Define | _: HandleBlock | _: End => b case _: Return | _: Break | _: Continue | _: HandleBlockReturn | _: Throw => super.applyBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 30b443810..cb1bb6a73 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -266,6 +266,9 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): case blk @ AssignField(lhs, nme, rhs, rest) => val PartRet(head, parts) = go(rest) PartRet(AssignField(lhs, nme, rhs, head)(blk.symbol), parts) + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => + val PartRet(head, parts) = go(rest) + PartRet(AssignDynField(lhs, fld, arrayIdx, rhs, rest), parts) case Return(_, _) => PartRet(blk, Nil) // ignored cases case TryBlock(sub, finallyDo, rest) => ??? // ignore From 206df9dc201f99b1bb2bf7a2adb1d39ffa8eb2d6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 31 Jan 2025 17:19:11 +0800 Subject: [PATCH 034/127] update tests --- .../src/test/mlscript-compile/Option.mjs | 8 +++-- .../src/test/mlscript-compile/Predef.mjs | 36 ++++++++++++++----- .../src/test/mlscript-compile/Stack.mjs | 4 ++- .../test/mlscript-compile/apps/Accounting.mjs | 12 +++++-- .../src/test/mlscript-compile/apps/CSV.mjs | 4 ++- 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript-compile/Option.mjs b/hkmc2/shared/src/test/mlscript-compile/Option.mjs index a22d93fb7..c259eed88 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Option.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Option.mjs @@ -2,7 +2,9 @@ import Predef from "./Predef.mjs"; let Option1; Option1 = class Option { static { - this.Some = function Some(value1) { return new Some.class(value1); }; + this.Some = function Some(value1) { + return new Some.class(value1); + }; this.Some.class = class Some { constructor(value) { this.value = value; @@ -15,7 +17,9 @@ Option1 = class Option { }; this.None = new None$class; this.None.class = None$class; - this.Both = function Both(fst1, snd1) { return new Both.class(fst1, snd1); }; + this.Both = function Both(fst1, snd1) { + return new Both.class(fst1, snd1); + }; this.Both.class = class Both { constructor(fst, snd) { this.fst = fst; diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index c70d679b4..87495c33f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -3,14 +3,18 @@ Predef1 = class Predef { static { this.assert = globalThis.console.assert; this.foldl = Predef.fold; - this.MatchResult = function MatchResult(captures1) { return new MatchResult.class(captures1); }; + this.MatchResult = function MatchResult(captures1) { + return new MatchResult.class(captures1); + }; this.MatchResult.class = class MatchResult { constructor(captures) { this.captures = captures; } toString() { return "MatchResult(" + globalThis.Predef.render(this.captures) + ")"; } }; - this.MatchFailure = function MatchFailure(errors1) { return new MatchFailure.class(errors1); }; + this.MatchFailure = function MatchFailure(errors1) { + return new MatchFailure.class(errors1); + }; this.MatchFailure.class = class MatchFailure { constructor(errors) { this.errors = errors; @@ -68,21 +72,27 @@ Predef1 = class Predef { } toString() { return "Test"; } }; - this.__Cont = function __Cont(next1) { return new __Cont.class(next1); }; + this.__Cont = function __Cont(next1) { + return new __Cont.class(next1); + }; this.__Cont.class = class __Cont { constructor(next) { this.next = next; } toString() { return "__Cont(" + globalThis.Predef.render(this.next) + ")"; } }; - this.__TailList = function __TailList(next1) { return new __TailList.class(next1); }; + this.__TailList = function __TailList(next1) { + return new __TailList.class(next1); + }; this.__TailList.class = class __TailList { constructor(next) { this.next = next; } toString() { return "__TailList(" + globalThis.Predef.render(this.next) + ")"; } }; - this.__ListWithTail = function __ListWithTail(next1, tail1) { return new __ListWithTail.class(next1, tail1); }; + this.__ListWithTail = function __ListWithTail(next1, tail1) { + return new __ListWithTail.class(next1, tail1); + }; this.__ListWithTail.class = class __ListWithTail { constructor(next, tail) { this.next = next; @@ -95,7 +105,9 @@ Predef1 = class Predef { } toString() { return "__ListWithTail(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.tail) + ")"; } }; - this.__HandleBlock = function __HandleBlock(contHead1, lastHandlerCont1, next1, handler1) { return new __HandleBlock.class(contHead1, lastHandlerCont1, next1, handler1); }; + this.__HandleBlock = function __HandleBlock(contHead1, lastHandlerCont1, next1, handler1) { + return new __HandleBlock.class(contHead1, lastHandlerCont1, next1, handler1); + }; this.__HandleBlock.class = class __HandleBlock { constructor(contHead, lastHandlerCont, next, handler) { this.contHead = contHead; @@ -105,7 +117,9 @@ Predef1 = class Predef { } toString() { return "__HandleBlock(" + globalThis.Predef.render(this.contHead) + ", " + globalThis.Predef.render(this.lastHandlerCont) + ", " + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.handler) + ")"; } }; - this.__EffectSig = function __EffectSig(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1) { return new __EffectSig.class(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1); }; + this.__EffectSig = function __EffectSig(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1) { + return new __EffectSig.class(next1, tail1, handleBlockList1, resumed1, handler1, handlerFun1); + }; this.__EffectSig.class = class __EffectSig { constructor(next, tail, handleBlockList, resumed, handler, handlerFun) { this.next = next; @@ -117,7 +131,9 @@ Predef1 = class Predef { } toString() { return "__EffectSig(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.tail) + ", " + globalThis.Predef.render(this.handleBlockList) + ", " + globalThis.Predef.render(this.resumed) + ", " + globalThis.Predef.render(this.handler) + ", " + globalThis.Predef.render(this.handlerFun) + ")"; } }; - this.__Return = function __Return(value1) { return new __Return.class(value1); }; + this.__Return = function __Return(value1) { + return new __Return.class(value1); + }; this.__Return.class = class __Return { constructor(value) { this.value = value; @@ -128,7 +144,9 @@ Predef1 = class Predef { this.__stackDepth = 0; this.__stackOffset = 0; this.__stackHandler = null; - this.__StackDelay = function __StackDelay() { return new __StackDelay.class(); }; + this.__StackDelay = function __StackDelay() { + return new __StackDelay.class(); + }; this.__StackDelay.class = class __StackDelay { constructor() {} toString() { return "__StackDelay(" + "" + ")"; } diff --git a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs index b56ae448a..670427de1 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs @@ -2,7 +2,9 @@ import Predef from "./Predef.mjs"; let Stack1; Stack1 = class Stack { static { - this.Cons = function Cons(head1, tail1) { return new Cons.class(head1, tail1); }; + this.Cons = function Cons(head1, tail1) { + return new Cons.class(head1, tail1); + }; this.Cons.class = class Cons { constructor(head, tail) { this.head = head; diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs index 70930cd43..61fc1f88d 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs @@ -5,7 +5,9 @@ let Accounting1; Accounting1 = class Accounting { constructor() { this.warnings = []; - this.Project = function Project(num1) { return new Project.class(num1); }; + this.Project = function Project(num1) { + return new Project.class(num1); + }; this.Project.class = class Project { constructor(num) { this.num = num; @@ -13,7 +15,9 @@ Accounting1 = class Accounting { toString() { return "Project(" + globalThis.Predef.render(this.num) + ")"; } }; const this$Accounting = this; - this.Line = function Line(name1, proj1, starting_balance1, isMatchable1) { return new Line.class(name1, proj1, starting_balance1, isMatchable1); }; + this.Line = function Line(name1, proj1, starting_balance1, isMatchable1) { + return new Line.class(name1, proj1, starting_balance1, isMatchable1); + }; this.Line.class = class Line { constructor(name, proj, starting_balance, isMatchable) { this.name = name; @@ -45,7 +49,9 @@ Accounting1 = class Accounting { toString() { return "Line(" + globalThis.Predef.render(this.name) + ", " + globalThis.Predef.render(this.proj) + ", " + globalThis.Predef.render(this.starting_balance) + ", " + globalThis.Predef.render(this.isMatchable) + ")"; } }; this.lines = []; - this.Report = function Report(fileName1) { return new Report.class(fileName1); }; + this.Report = function Report(fileName1) { + return new Report.class(fileName1); + }; this.Report.class = class Report { constructor(fileName) { this.fileName = fileName; diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs index e07ba625d..b73999033 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/CSV.mjs @@ -1,7 +1,9 @@ import Str from "./../Str.mjs"; import Predef from "./../Predef.mjs"; let CSV1; -CSV1 = function CSV(strDelimiter1) { return new CSV.class(strDelimiter1); }; +CSV1 = function CSV(strDelimiter1) { + return new CSV.class(strDelimiter1); +}; CSV1.class = class CSV { constructor(strDelimiter) { this.strDelimiter = strDelimiter; From ae7aef96a402a6e5d4640385fe23a5dbef48bb01 Mon Sep 17 00:00:00 2001 From: Anson Yeung Date: Sat, 1 Feb 2025 01:24:23 +0800 Subject: [PATCH 035/127] Bug fixes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 9 +- .../src/test/mlscript/codegen/Lifter.mls | 140 +++++++++++------- 2 files changed, 93 insertions(+), 56 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 2f8c7c3f5..bd7e504bf 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -505,7 +505,12 @@ class Lifter(using State): case N => Lifted(d, Nil) case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms, singleCallBms)) => val createSym = d match - case d: ClsLikeDefn => ((nme: String) => TermSymbol(syntax.ParamBind, S(d.isym), Tree.Ident(nme))) + case d: ClsLikeDefn => + // due to the possibility of capturing a TempSymbol in HandlerLowering, it is necessary to generate a discriminator + val fresh = FreshInt() + (nme: String) => + val id = fresh.make + TermSymbol(syntax.ParamBind, S(d.isym), Tree.Ident(nme + "$" + id)) case _ => ((nme: String) => VarSymbol(Tree.Ident(nme))) val capturesSymbols = includedCaptures.map: sym => @@ -742,6 +747,6 @@ class Lifter(using State): case f: FunDefn => liftDefnsInFn(f, ctxx) case c: ClsLikeDefn => liftDefnsInCls(c, ctxx) case _ => return super.applyBlock(b) - (lifted :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) + (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(blk) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 93becbae7..f9aeebeb8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -415,6 +415,38 @@ fun f() = f() //│ = 1 +:sjs +fun f() = 3 +fun g() = + fun h() = 3 + h() +g() +//│ JS (unsanitized): +//│ let f15, g13, h4; +//│ f15 = function f() { +//│ return 3; +//│ }; +//│ h4 = function h() { +//│ return 3; +//│ }; +//│ g13 = function g() { +//│ return h4(); +//│ }; +//│ g13() +//│ = 3 + +// make sure class fields are discriminated properly for immutable captures when supported +:handler +abstract class Effect with + fun perform() +handle h = Effect with + fun perform()(k) = k() +fun f() = + h.perform() + 1 +f() + f() + f() +//│ = 3 + /// CLASSES /// :expect 1 @@ -430,63 +462,63 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let f15, Test1, g13, h4, tmp21, Test$ctor, Test$, g$11, h$4; +//│ let f17, Test1, g14, h5, tmp22, Test$ctor, Test$, g$11, h$4; //│ h$4 = function h$(used3, used1) { //│ return used3; //│ }; -//│ h4 = function h(used3, used1) { +//│ h5 = function h(used3, used1) { //│ return () => { //│ return h$4(used3, used1); //│ }; //│ }; //│ g$11 = function g$(used1, g_arg) { -//│ let used3, tmp22; +//│ let used3, tmp23; //│ used3 = 2; -//│ tmp22 = h$4(used3, used1); -//│ return used1 + tmp22; +//│ tmp23 = h$4(used3, used1); +//│ return used1 + tmp23; //│ }; -//│ g13 = function g(used1) { +//│ g14 = function g(used1) { //│ return (g_arg) => { //│ return g$11(used1, g_arg); //│ }; //│ }; -//│ Test$ = function Test$(used1, a1) { -//│ let tmp22; -//│ tmp22 = new Test1(a1); -//│ return tmp22(used1); +//│ Test$ = function Test$(used1$0, a1) { +//│ let tmp23; +//│ tmp23 = new Test1(a1); +//│ return tmp23(used1$0); //│ }; -//│ Test$ctor = function Test$ctor(used1) { +//│ Test$ctor = function Test$ctor(used1$0) { //│ return (a1) => { -//│ let tmp22; -//│ tmp22 = new Test1(a1); -//│ return tmp22(used1); +//│ let tmp23; +//│ tmp23 = new Test1(a1); +//│ return tmp23(used1$0); //│ }; //│ }; //│ Test1 = function Test(a2) { -//│ return (used11) => { -//│ return new Test.class(a2)(used11); +//│ return (used1$01) => { +//│ return new Test.class(a2)(used1$01); //│ } //│ }; //│ Test1.class = class Test { //│ constructor(a1) { -//│ return (used1) => { +//│ return (used1$0) => { //│ this.a = a1; -//│ this.used1 = used1; +//│ this.used1$0 = used1$0; //│ return this; //│ } //│ } //│ get() { -//│ return this.used1; +//│ return this.used1$0; //│ } //│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; -//│ f15 = function f(used1, unused1) { +//│ f17 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$(used1, unused1); //│ }; -//│ tmp21 = f15(1, 2); -//│ tmp21.get() ?? null +//│ tmp22 = f17(1, 2); +//│ tmp22.get() ?? null //│ = 1 :expect 1 @@ -548,41 +580,41 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f19, f20, g17, f$1, g$15, f$capture13; -//│ g$15 = function g$(f$capture14) { +//│ let f22, f23, g18, f$2, g$15, f$capture15; +//│ g$15 = function g$(f$capture16) { //│ return 1; //│ }; -//│ g17 = function g(f$capture14) { +//│ g18 = function g(f$capture16) { //│ return () => { -//│ return g$15(f$capture14); +//│ return g$15(f$capture16); //│ }; //│ }; -//│ f$1 = function f$(f$capture14) { -//│ f$capture14.x0$ = 1; +//│ f$2 = function f$(f$capture16) { +//│ f$capture16.x0$ = 1; //│ return null; //│ }; -//│ f20 = function f(f$capture14) { +//│ f23 = function f(f$capture16) { //│ return () => { -//│ return f$1(f$capture14); +//│ return f$2(f$capture16); //│ }; //│ }; -//│ f$capture13 = function f$capture(x0$1) { +//│ f$capture15 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); //│ }; -//│ f$capture13.class = class f$capture12 { +//│ f$capture15.class = class f$capture14 { //│ constructor(x0$) { //│ this.x0$ = x0$; //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f19 = function f() { -//│ let tmp26, capture; -//│ capture = new f$capture13(null); +//│ f22 = function f() { +//│ let tmp27, capture; +//│ capture = new f$capture15(null); //│ capture.x0$ = 1; -//│ tmp26 = f$1(capture); +//│ tmp27 = f$2(capture); //│ return capture.x0$; //│ }; -//│ f19() +//│ f22() //│ = 1 // don't break private variables @@ -622,37 +654,37 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let g19, f22, h7, tmp28, f$2, h$7, g$capture1, f$capture15; -//│ h$7 = function h$(f$capture16, g$capture2) { -//│ f$capture16.k0$ = 5; -//│ f$capture16.x1$ = 4; -//│ return f$capture16.x1$ + g$capture2.y0$; +//│ let g20, f24, h8, tmp29, f$3, h$7, g$capture1, f$capture17; +//│ h$7 = function h$(f$capture18, g$capture2) { +//│ f$capture18.k0$ = 5; +//│ f$capture18.x1$ = 4; +//│ return f$capture18.x1$ + g$capture2.y0$; //│ }; -//│ h7 = function h(f$capture16, g$capture2) { +//│ h8 = function h(f$capture18, g$capture2) { //│ return () => { -//│ return h$7(f$capture16, g$capture2); +//│ return h$7(f$capture18, g$capture2); //│ }; //│ }; -//│ f$capture15 = function f$capture(k0$1, x1$1) { +//│ f$capture17 = function f$capture(k0$1, x1$1) { //│ return new f$capture.class(k0$1, x1$1); //│ }; -//│ f$capture15.class = class f$capture14 { +//│ f$capture17.class = class f$capture16 { //│ constructor(k0$, x1$) { //│ this.k0$ = k0$; //│ this.x1$ = x1$; //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.k0$) + ", " + globalThis.Predef.render(this.x1$) + ")"; } //│ }; -//│ f$2 = function f$(g$capture2, x1) { +//│ f$3 = function f$(g$capture2, x1) { //│ let capture; -//│ capture = new f$capture15(null, x1); +//│ capture = new f$capture17(null, x1); //│ capture.k0$ = 4; //│ g$capture2.y0$ = 2; //│ return capture.x1$; //│ }; -//│ f22 = function f(g$capture2) { +//│ f24 = function f(g$capture2) { //│ return (x1) => { -//│ return f$2(g$capture2, x1); +//│ return f$3(g$capture2, x1); //│ }; //│ }; //│ g$capture1 = function g$capture(y0$1) { @@ -664,12 +696,12 @@ g()(1) //│ } //│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } //│ }; -//│ g19 = function g() { +//│ g20 = function g() { //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f22(capture) ?? null; +//│ return f24(capture) ?? null; //│ }; -//│ tmp28 = g19(); -//│ tmp28(1) ?? null +//│ tmp29 = g20(); +//│ tmp29(1) ?? null //│ = 1 From 09eb9b71c93b3f38174810b49c10b1654f443e1c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Feb 2025 20:21:08 +0800 Subject: [PATCH 036/127] add more advanced analysis --- .../src/main/scala/hkmc2/codegen/Block.scala | 1 + .../src/main/scala/hkmc2/codegen/Lifter.scala | 341 +++++++++++------- hkmc2/shared/src/test/mlscript/HkScratch.mls | 74 +++- .../src/test/mlscript/codegen/Lifter.mls | 172 ++++++--- 4 files changed, 414 insertions(+), 174 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index a37be7c88..0a6223c20 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -269,6 +269,7 @@ sealed abstract class Result: case Value.Lit(lit) => Set.empty case Value.Lam(params, body) => body.freeVars -- params.paramSyms case Value.Arr(elems) => elems.flatMap(_.value.freeVars).toSet + case DynSelect(qual, fld, arrayIdx) => qual.freeVars ++ fld.freeVars // type Local = LocalSymbol type Local = Symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 2f8c7c3f5..b6150a91d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -15,19 +15,26 @@ import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet -/** - * Lifts classes and functions to the top-level. - * Assumes the input block does not have any `HandleBlock`s and lamdbas are - * rewritten as functions (lambdas will be removed from the IR soon). - */ -class Lifter(using State): - +object Lifter: /** * Describes the free variables of a function that have been accessed by nested definitions. - * @param vars The free variables that are accessed or mutated by nested classes/functions. - * @param mutated The free variables that are mutated, but not accessed, by nested classes/functions. + * @param vars The free variables that are accessed by nested classes/functions. + * @param reqCapture The free variables that must be captured using a heap-allocated object. */ - case class FreeVars(vars: List[Local], mutated: List[Local]) + case class FreeVars(vars: List[Local], reqCapture: List[Local]) + + def getVars(f: FunDefn): Set[Local] = + (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: + case s: FlowSymbol => s + +/** + * 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): + import Lifter.FreeVars + + val varAnalyzer = new UsedVarAnalyzer // use mutable sets locally to avoid reconstructing everything // linked hash sets preserve order @@ -102,98 +109,6 @@ class Lifter(using State): def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) - - def getVars(f: FunDefn): Set[Local] = - (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: - case s: FlowSymbol => s - - /** - * Given a function definition `f` and previously bound locals `lookup`, - * creates a map `FunDefn` -> `List[Local]` where each function definition f - * in boundLocals is associated with a list of locals which both: - * - First occur in f, i.e. is a free variable of f, - * - Are accessed by some definition within f. - * - * These are the variables which will be moved to the capture. - * We do this once for every top-level function definition instead - * of once for every function definition so that we only traverse - * the tree once. - * - * @param f The function in which to find used locals. - * @param lookup The map describing which function a particular local was defined by. - * @return The used locals of `f` and all its nested definitions. - */ - private def findUsedLocalsImpl(f: FunDefn, lookup: Map[Local, FunDefn]): Map[BlockMemberSymbol, FreeVarsMut] = - val definedVars = getVars(f) - - // add this function's locals to the lookup map - // NOTE: `lookup` will overwrite definitions already defined in previous functions - // here, ++ must not be used as a commutative operator! - val lookupNext = definedVars.map(_ -> f).toMap ++ lookup - - // collect all function definitions - val retMap: MutMap[BlockMemberSymbol, FreeVarsMut] = MutMap.from(lookupNext.map: - case _ -> f => f.sym -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty) - ) - - // add this function in case this function has no locals - if !retMap.contains(f.sym) then retMap.addOne(f.sym -> FreeVarsMut(LinkedHashSet.empty, LinkedHashSet.empty)) - - // merge recursive call results - def merge(next: Map[BlockMemberSymbol, FreeVarsMut]) = - for f -> (v @ FreeVarsMut(vars, mutated)) <- next do retMap.get(f) match - case None => retMap.addOne(f -> v) - case Some(value) => - for l <- vars do retMap(f).vars.addOne(l) - for l <- mutated do retMap(f).mutated.addOne(l) - - // tracks if the locals here have been mutated more than once in this function - val assignedOnce: MutSet[Local] = MutSet.empty - val assignedTwice: MutSet[Local] = MutSet.empty - - def addLocal(l: Local, mut: Bool) = lookup.get(l) match - case Some(f) => - if mut then retMap(f.sym).mutated.addOne(l) - retMap(f.sym).vars.addOne(l) - case None => if mut then - if assignedOnce.contains(l) then - assignedTwice.add(l) - else - assignedOnce.add(l) - - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Define(f: FunDefn, rest) => - merge(findUsedLocalsImpl(f, lookupNext)) - super.applyBlock(b) - case Define(c: ClsLikeDefn, rest) => - for f <- c.methods do merge(findUsedLocalsImpl(f, lookupNext)) - super.applyBlock(b) - case Assign(lhs, rhs, rest) => - addLocal(lhs, true) // TODO: when proper immutable variables have been added, refactor this - applyResult(rhs) - super.applyBlock(b) - case _ => super.applyBlock(b) - - override def applyValue(v: Value): Value = v match - case Value.Ref(l) => - addLocal(l, false) - super.applyValue(v) - - case _ => super.applyValue(v) - - walker.applyBlock(f.body) - - // add mutable locals - retMap(f.sym).mutated ++= retMap(f.sym).vars.intersect(assignedTwice) - - retMap.toMap - - private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = - c.methods.map(findUsedLocalsImpl(_, Map.empty)) - .foldLeft(findUsedLocals(c.preCtor).mp ++ findUsedLocals(c.ctor).mp): - case (acc, newMap) => acc ++ newMap.map: - case defn -> freeVars => defn -> freeVars.toImmut /** * Finds the used locals of functions which have been used by their nested definitions. @@ -203,18 +118,10 @@ class Lifter(using State): */ def findUsedLocals(b: Block): UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Define(f: FunDefn, rest) => - val m = findUsedLocalsImpl(f, Map.empty).map: - case f -> FreeVarsMut(vars, mutated) => - f -> FreeVars(vars.toList, mutated.toList) - usedMap ++= m - super.applyBlock(b) - case Define(c: ClsLikeDefn, rest) => - usedMap ++= findUsedLocalsCls(c) - super.applyBlock(b) - case _ => super.applyBlock(b) + val walker = new BlockTransformer(SymbolSubst()): + override def applyFunDefn(fun: FunDefn): FunDefn = + usedMap += (fun.sym -> varAnalyzer.reqdCaptureLocals(fun)) + super.applyFunDefn(fun) walker.applyBlock(b) UsedLocalsMap.from(usedMap) @@ -234,16 +141,16 @@ class Lifter(using State): Tree.Ident(nme) ) - val FreeVars(vars, mutated) = ctx.usedLocals(f.sym) + val FreeVars(_, cap) = ctx.usedLocals(f.sym) val fresh = FreshInt() - val varsMap: Map[Local, TermSymbol] = mutated.map: s => + val varsMap: Map[Local, TermSymbol] = cap.map: s => val id = fresh.make s -> TermSymbol(syntax.ParamBind, S(clsSym), Tree.Ident(s.nme + id + "$")) .toMap - val varsList = mutated.toList + val varsList = cap.toList val defn = ClsLikeDefn( None, clsSym, BlockMemberSymbol(nme, Nil), @@ -293,7 +200,7 @@ class Lifter(using State): */ private def needsCapture(captureFn: FunDefn, liftDefn: Defn, ctx: LifterCtx) = val candVars = liftDefn.freeVars - val captureFnVars = ctx.usedLocals(captureFn.sym).mutated.toSet + val captureFnVars = ctx.usedLocals(captureFn.sym).reqCapture.toSet !candVars.intersect(captureFnVars).isEmpty /** @@ -305,7 +212,7 @@ class Lifter(using State): private def neededImutLocals(captureFn: FunDefn, liftDefn: Defn, ctx: LifterCtx) = val candVars = liftDefn.freeVars val captureFnVars = ctx.usedLocals(captureFn.sym) - val mutVars = captureFnVars.mutated.toSet + val mutVars = captureFnVars.reqCapture.toSet val imutVars = captureFnVars.vars imutVars.filter: s => !mutVars.contains(s) && candVars.contains(s) @@ -324,10 +231,16 @@ class Lifter(using State): ) def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + /* + the commented code is incorrect, more in-depth analysis is needed to properly remove variables/captures + that aren't needed. For example, + fun f() = + fun g(x) = x + fun h(y) = y + both g and h both capture x and y, but this is not needed. + + Incorrect code: - // the commented code is incorrect, more in-depth analysis is needed to properly remove variables/captures - // that aren't needed - /* val includedCaptures = ctx.prevFnDefns.filter(needsCapture(_, d, ctx)) val includedLocals = ctx.prevFnDefns.flatMap: ls => @@ -342,12 +255,12 @@ class Lifter(using State): // for now, we just include everything val includedCaptures = ctx.prevFnDefns.filter: f => - val FreeVars(vars, mut) = ctx.usedLocals(f.sym) - mut.size != 0 + val FreeVars(vars, cap) = ctx.usedLocals(f.sym) + cap.size != 0 val includedLocals = ctx.prevFnDefns.flatMap: f => - val FreeVars(vars, mut) = ctx.usedLocals(f.sym) - vars.filter(!mut.contains(_)) + val FreeVars(vars, cap) = ctx.usedLocals(f.sym) + vars.filter(!cap.contains(_)) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) @@ -631,7 +544,7 @@ class Lifter(using State): val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) - case _ => Lifted(d, Nil) + case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns @@ -686,11 +599,11 @@ class Lifter(using State): // some book-keeping val thisVars = ctx.usedLocals(f.sym) val newCtx = captureCtx - .addLocalPaths((thisVars.vars.toSet -- thisVars.mutated).map(s => s -> s).toMap) + .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> s).toMap) val transformed = rewriteBlk(blk, N, newCtx) - if thisVars.mutated.size == 0 then + if thisVars.reqCapture.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) else // move the function's parameters to the capture @@ -745,3 +658,173 @@ class Lifter(using State): (lifted :: extra).foldLeft(rest)((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker.applyBlock(blk) + +/** + * 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 + * allocation (during class lifting) despite being mutable. + * + * Assumes the input trees have no lambdas. + */ +class UsedVarAnalyzer: + import Lifter.FreeVars + + // TODO: these two functions are orthogonal and could be combined into one, + // which reduces the number of passes over the tree by one + + private val usedVarsCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty + def getUsedVars(f: FunDefn): Set[Local] = usedVarsCache.get(f.sym) match + case Some(value) => value + case None => + val fVars = Lifter.getVars(f) + var usedVars: Set[Local] = Set.empty + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = + usedVars ++= fVars.intersect(defn.freeVars) + super.applyDefn(defn) + + walker.applyBlock(f.body) + + usedVarsCache.addOne(f.sym -> usedVars) + fVars + + private val blkMutCache: MutMap[Local, Set[Local]] = MutMap.empty + private def findBlkMutations(b: Block, cacheId: Opt[Local] = N): Set[Local] = + cacheId.flatMap(blkMutCache.get) match + case Some(value) => value + case None => + var mutated: Set[Local] = Set.empty + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Assign(lhs, rhs, rest) => + mutated += lhs + applyBlock(rest) + case Label(label, body, rest) => + mutated ++= findBlkMutations(body, S(label)) + applyBlock(rest) + case _ => super.applyBlock(b) + + override def applyDefn(defn: Defn): Defn = + mutated ++= findMutations(defn) + super.applyDefn(defn) + + walker.applyBlock(b) + + cacheId match + case None => () + case Some(value) => blkMutCache.addOne(value -> mutated) + + mutated + + private val mutatedCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty + + /** + * Finds the variables which this definition could possibly mutate. + * + * @param defn The definition to search through + * @return The variables which this definition could possibly mutate. + */ + def findMutations(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match + case Some(value) => value + case None => defn match + case f: FunDefn => findBlkMutations(f.body) + case c: ClsLikeDefn => + findBlkMutations(c.preCtor) ++ findBlkMutations(c.ctor) ++ c.methods.flatMap(findMutations) + case _: ValDefn => Set.empty + + // 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. + def reqdCaptureLocals(f: FunDefn) = + var (_, defns) = f.body.floatOutDefns + val defnSyms = defns.collect: + case f: FunDefn => f.sym -> f + case c: ClsLikeDefn => c.sym -> c + .toMap + + val thisVars = Lifter.getVars(f) + + 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 = + var reqCapture = reqCapture_ + var hasReader = hasReader_ + var hasMutator = hasMutator_ + + inline def merge(c: CaptureInfo) = + reqCapture ++= c.reqCapture + hasReader ++= c.hasReader + hasMutator ++= c.hasMutator + + inline def rec(b: Block) = go(b, reqCapture, hasReader, hasMutator) + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Assign(lhs, rhs, rest) => + applyResult(rhs) + if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs + super.applyBlock(rest) + + case Match(scrut, arms, dflt, rest) => + applyPath(scrut) + val infos = arms.map: + case (_, arm) => rec(arm) + val dfltInfo = dflt.map: + case arm => rec(arm) |> merge + + infos.map(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive + applyBlock(rest) + b + case Label(label, 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(findBlkMutations(body, S(label))) + reqCapture ++= mut.intersect(body.freeVars) + applyBlock(rest) + b + case Begin(sub, rest) => + rec(sub) |> merge + applyBlock(rest) + b + case TryBlock(sub, finallyDo, rest) => + // sub and finallyDo could be executed sequentially, so we must merge + rec(sub) |> merge + rec(finallyDo) |> merge + applyBlock(rest) + b + case _ => super.applyBlock(b) + + override def applyValue(v: Value): Value = v match + case Value.Ref(l: BlockMemberSymbol) => + defnSyms.get(l) match + case None => super.applyValue(v) + case Some(defn) => + val muts = findMutations(defn).intersect(thisVars) + val reads = defn.freeVars.intersect(thisVars) -- muts + // functions mutating a variable will always need a capture + for l <- muts do + if hasReader.contains(l) || hasMutator.contains(l) || defn.isInstanceOf[FunDefn] then + reqCapture += l + hasMutator += l + for l <- reads do + if hasMutator.contains(l) then + reqCapture += l + hasReader += l + v + case Value.Ref(l) => + if hasMutator.contains(l) then reqCapture += (l) + v + case _ => super.applyValue(v) + + walker.applyBlock(f.body) + + CaptureInfo(reqCapture, hasReader, hasMutator) + + val reqCapture = go(f.body, Set.empty, Set.empty, Set.empty).reqCapture + val usedVars = defns.flatMap(_.freeVars.intersect(thisVars)).toSet + Lifter.FreeVars(usedVars.toList, reqCapture.toList) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..bf09024da 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,5 +7,75 @@ :global :d - - +:sjs +:lift +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + Test(unused1) +f(1, 2).get() +//│ Elab: { ‹› fun member:f‹179›(Param(‹›,used1‹180›,None), Param(‹›,unused1‹181›,None), ) = { ‹› fun member:g‹183›(Param(‹›,g_arg‹184›,None), ) = { ‹› fun member:h‹185› = used3‹186›#666; let used3‹186›; used3‹186› = 2; builtin:+‹16›#0(used1‹180›#666, member:h‹185›#666) }; let unused2‹190›; unused2‹190› = 2; Cls TestParamList(‹›,List(Param(‹›,class:Test‹191›.a,None)),None) { ‹› fun member:get‹176›() = used1‹180›#666; }; member:Test‹182›#666(unused1‹181›#666) }; member:f‹179›#666(1, 2).get() } +//│ JS (unsanitized): +//│ let f, Test1, g, h, tmp, Test$ctor, Test$, g$, h$; +//│ h$ = function h$(used3, used1) { +//│ return used3; +//│ }; +//│ h = function h(used3, used1) { +//│ return () => { +//│ return h$(used3, used1); +//│ }; +//│ }; +//│ g$ = function g$(used1, g_arg) { +//│ let used3, tmp1; +//│ used3 = 2; +//│ tmp1 = h$(used3, used1); +//│ return used1 + tmp1; +//│ }; +//│ g = function g(used1) { +//│ return (g_arg) => { +//│ return g$(used1, g_arg); +//│ }; +//│ }; +//│ Test$ = function Test$(used1, a) { +//│ let tmp1; +//│ tmp1 = new Test1(a); +//│ return tmp1(used1); +//│ }; +//│ Test$ctor = function Test$ctor(used1) { +//│ return (a) => { +//│ let tmp1; +//│ tmp1 = new Test1(a); +//│ return tmp1(used1); +//│ }; +//│ }; +//│ Test1 = function Test(a1) { +//│ return (used11) => { +//│ return new Test.class(a1)(used11); +//│ } +//│ }; +//│ Test1.class = class Test { +//│ constructor(a) { +//│ return (used1) => { +//│ this.a = a; +//│ this.used1 = used1; +//│ return this; +//│ } +//│ } +//│ get() { +//│ return this.used1; +//│ } +//│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } +//│ }; +//│ f = function f(used1, unused1) { +//│ let unused2; +//│ unused2 = 2; +//│ return Test$(used1, unused1); +//│ }; +//│ tmp = f(1, 2); +//│ tmp.get() ?? null +//│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index 93becbae7..a3c93cd33 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -151,7 +151,7 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let f5, g5, g$4; -//│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { +//│ g$4 = function g$(a4, a3, a6, a2, a1, a5) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; @@ -159,14 +159,14 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6; //│ }; -//│ g5 = function g(a1, a2, a3, a4, a5, a6) { +//│ g5 = function g(a4, a3, a6, a2, a1, a5) { //│ return () => { -//│ return g$4(a1, a2, a3, a4, a5, a6); +//│ return g$4(a4, a3, a6, a2, a1, a5); //│ }; //│ }; //│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$4(a1, a2, a3, a4, a5, a6); +//│ tmp = g$4(a4, a3, a6, a2, a1, a5); //│ return tmp; //│ }; //│ f5(1, 2, 3, 4, 5, 6) @@ -621,55 +621,141 @@ fun g() = x f g()(1) +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: java.util.NoSuchElementException: None.get +//│ at: scala.None$.get(Option.scala:627) +//│ at: scala.None$.get(Option.scala:626) +//│ at: hkmc2.Lifter$$anon$5.applyPath(Lifter.scala:366) +//│ at: hkmc2.codegen.BlockTransformer.applyArg(BlockTransformer.scala:174) +//│ at: hkmc2.codegen.BlockTransformer.$anonfun$5(BlockTransformer.scala:99) +//│ at: scala.collection.immutable.List.mapConserve(List.scala:473) +//│ at: hkmc2.codegen.BlockTransformer.applyResult(BlockTransformer.scala:99) +//│ at: hkmc2.codegen.BlockTransformer.applyResult2(BlockTransformer.scala:94) +//│ at: hkmc2.codegen.BlockTransformer.applyBlock(BlockTransformer.scala:25) +//│ at: hkmc2.codegen.BlockTransformerShallow.applyBlock(BlockTransformer.scala:237) + +// only w should be in a capture +:expect 10111 +:sjs +fun f() = + let x = 1 + let y = 10 + let z = 10 + let w = 1000 + class Good() with + fun foo() = + set z = 100 + x + y + z + w + class Bad() with + fun foo() = + set w = 10000 + Bad().foo() + Good() +f().foo() //│ JS (unsanitized): -//│ let g19, f22, h7, tmp28, f$2, h$7, g$capture1, f$capture15; -//│ h$7 = function h$(f$capture16, g$capture2) { -//│ f$capture16.k0$ = 5; -//│ f$capture16.x1$ = 4; -//│ return f$capture16.x1$ + g$capture2.y0$; +//│ let f22, Bad1, Good1, tmp28, Bad$ctor, Bad$, Good$ctor, Good$, f$capture15; +//│ Good$ = function Good$(x1, z, y1, f$capture16) { +//│ let tmp29; +//│ tmp29 = new Good1(); +//│ return tmp29(x1, z, y1, f$capture16); //│ }; -//│ h7 = function h(f$capture16, g$capture2) { +//│ Good$ctor = function Good$ctor(x1, z, y1, f$capture16) { //│ return () => { -//│ return h$7(f$capture16, g$capture2); +//│ let tmp29; +//│ tmp29 = new Good1(); +//│ return tmp29(x1, z, y1, f$capture16); //│ }; //│ }; -//│ f$capture15 = function f$capture(k0$1, x1$1) { -//│ return new f$capture.class(k0$1, x1$1); +//│ Good1 = function Good() { +//│ return (x2, z1, y2, f$capture17) => { +//│ return new Good.class()(x2, z1, y2, f$capture17); +//│ } //│ }; -//│ f$capture15.class = class f$capture14 { -//│ constructor(k0$, x1$) { -//│ this.k0$ = k0$; -//│ this.x1$ = x1$; +//│ Good1.class = class Good { +//│ constructor() { +//│ return (x1, z, y1, f$capture16) => { +//│ this.x = x1; +//│ this.z = z; +//│ this.y = y1; +//│ this.f$capture = f$capture16; +//│ return this; +//│ } +//│ } +//│ foo() { +//│ let tmp29, tmp30; +//│ this.z = 100; +//│ tmp29 = this.x + this.y; +//│ tmp30 = tmp29 + this.z; +//│ return tmp30 + this.f$capture.w0$; //│ } -//│ toString() { return "f$capture(" + globalThis.Predef.render(this.k0$) + ", " + globalThis.Predef.render(this.x1$) + ")"; } -//│ }; -//│ f$2 = function f$(g$capture2, x1) { -//│ let capture; -//│ capture = new f$capture15(null, x1); -//│ capture.k0$ = 4; -//│ g$capture2.y0$ = 2; -//│ return capture.x1$; -//│ }; -//│ f22 = function f(g$capture2) { -//│ return (x1) => { -//│ return f$2(g$capture2, x1); +//│ toString() { return "Good(" + "" + ")"; } +//│ }; +//│ Bad$ = function Bad$(x1, z, y1, f$capture16) { +//│ let tmp29; +//│ tmp29 = new Bad1(); +//│ return tmp29(x1, z, y1, f$capture16); +//│ }; +//│ Bad$ctor = function Bad$ctor(x1, z, y1, f$capture16) { +//│ return () => { +//│ let tmp29; +//│ tmp29 = new Bad1(); +//│ return tmp29(x1, z, y1, f$capture16); //│ }; //│ }; -//│ g$capture1 = function g$capture(y0$1) { -//│ return new g$capture.class(y0$1); +//│ Bad1 = function Bad() { +//│ return (x2, z1, y2, f$capture17) => { +//│ return new Bad.class()(x2, z1, y2, f$capture17); +//│ } //│ }; -//│ g$capture1.class = class g$capture { -//│ constructor(y0$) { -//│ this.y0$ = y0$; +//│ Bad1.class = class Bad { +//│ constructor() { +//│ return (x1, z, y1, f$capture16) => { +//│ this.x = x1; +//│ this.z = z; +//│ this.y = y1; +//│ this.f$capture = f$capture16; +//│ return this; +//│ } //│ } -//│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } +//│ foo() { +//│ this.f$capture.w0$ = 10000; +//│ return null; +//│ } +//│ toString() { return "Bad(" + "" + ")"; } //│ }; -//│ g19 = function g() { -//│ let capture; -//│ capture = new g$capture1(null); -//│ capture.y0$ = 0; -//│ return f22(capture) ?? null; +//│ f$capture15 = function f$capture(w0$1) { +//│ return new f$capture.class(w0$1); //│ }; -//│ tmp28 = g19(); -//│ tmp28(1) ?? null -//│ = 1 +//│ f$capture15.class = class f$capture14 { +//│ constructor(w0$) { +//│ this.w0$ = w0$; +//│ } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } +//│ }; +//│ f22 = function f() { +//│ let x1, y1, z, tmp29, tmp30, capture; +//│ capture = new f$capture15(null); +//│ x1 = 1; +//│ y1 = 10; +//│ z = 10; +//│ capture.w0$ = 1000; +//│ tmp29 = Bad$(x1, z, y1, capture); +//│ tmp30 = tmp29.foo() ?? null; +//│ return Good$(x1, z, y1, capture); +//│ }; +//│ tmp28 = f22(); +//│ tmp28.foo() ?? null +//│ = 10111 + +:expect 2 +fun f() = + let b = 0 + fun g() = + set b += 1 + b + g +let ret = f() +ret() +ret() +//│ = 2 +//│ ret = [Function (anonymous)] From 406058bbf60f161f1a42087f40e4aca0f93bf3be Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Feb 2025 20:42:54 +0800 Subject: [PATCH 037/127] add test --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index b6150a91d..54e6c7b1c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -757,23 +757,25 @@ class UsedVarAnalyzer: hasReader ++= c.hasReader hasMutator ++= c.hasMutator - inline def rec(b: Block) = go(b, reqCapture, hasReader, hasMutator) + def rec(blk: Block) = + go(blk, reqCapture, hasReader, hasMutator) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs - super.applyBlock(rest) + applyBlock(rest) case Match(scrut, arms, dflt, rest) => applyPath(scrut) val infos = arms.map: case (_, arm) => rec(arm) val dfltInfo = dflt.map: - case arm => rec(arm) |> merge + case arm => rec(arm) infos.map(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive + dfltInfo.map(merge) applyBlock(rest) b case Label(label, body, rest) => @@ -821,7 +823,7 @@ class UsedVarAnalyzer: v case _ => super.applyValue(v) - walker.applyBlock(f.body) + walker.applyBlock(b) CaptureInfo(reqCapture, hasReader, hasMutator) From 8c5e8dc589e0839de6c8c7aecc9095a712bd82b3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 1 Feb 2025 23:31:56 +0800 Subject: [PATCH 038/127] Fix bugs --- .../src/main/scala/hkmc2/codegen/Block.scala | 66 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 132 ++-- .../scala/hkmc2/codegen/llir/Builder.scala | 2 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 74 +- .../src/test/mlscript/codegen/Lifter.mls | 92 ++- .../src/test/mlscript/handlers/Effects.mls | 250 ++++++ .../test/mlscript/handlers/StackSafety.mls | 718 ++++++++++++------ 7 files changed, 941 insertions(+), 393 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 0a6223c20..909b6f3db 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -55,6 +55,7 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, fin, rst) => 1 + sub.size + fin.size + rst.size case Label(_, bod, rst) => 1 + bod.size + rst.size case HandleBlock(lhs, res, par, cls, handlers, bdy, rst) => 1 + handlers.map(_.body.size).sum + bdy.size + rst.size + case AssignDynField(l, n, ai, r, rst) => 1 + rst.size // TODO conserve if no changes def mapTail(f: BlockTail => BlockTail): Block = this match @@ -68,7 +69,7 @@ sealed abstract class Block extends Product with AutoLocated: Match(scrut, arms.map(_ -> _.mapTail(f)), dflt.map(_.mapTail(f)), rst) case Match(scrut, arms, dflt, rst) => Match(scrut, arms, dflt, rst.mapTail(f)) - + lazy val freeVars: Set[Local] = this match case Match(scrut, arms, dflt, rest) => scrut.freeVars ++ dflt.toList.flatMap(_.freeVars) ++ rest.freeVars @@ -83,12 +84,37 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, finallyDo, rest) => sub.freeVars ++ finallyDo.freeVars ++ rest.freeVars case Assign(lhs, rhs, rest) => Set(lhs) ++ rhs.freeVars ++ rest.freeVars case AssignField(lhs, nme, rhs, rest) => lhs.freeVars ++ rhs.freeVars ++ rest.freeVars - case Define(defn, rest) => defn.freeVars ++ rest.freeVars + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVars ++ fld.freeVars ++ rhs.freeVars ++ rest.freeVars + case Define(defn, rest) => defn.freeVarsLLIR ++ rest.freeVars case HandleBlock(lhs, res, par, cls, hdr, bod, rst) => (bod.freeVars - lhs) ++ rst.freeVars ++ hdr.flatMap(_.freeVars) case HandleBlockReturn(res) => res.freeVars case End(msg) => Set.empty + // TODO: freeVarsLLIR skips `fun` and `cls` in `Call` and `Instantiate` respectively, which is needed in some + // other places. However, adding them breaks some LLIR tests. Supposedly, once the IR uses the new symbol system, + // this should no longer happen. This version should be removed once that is resolved. + lazy val freeVarsLLIR: Set[Local] = this match + case Match(scrut, arms, dflt, rest) => + scrut.freeVarsLLIR ++ dflt.toList.flatMap(_.freeVarsLLIR) ++ rest.freeVarsLLIR + ++ arms.flatMap: + (pat, arm) => arm.freeVarsLLIR -- pat.freeVars + case Return(res, implct) => res.freeVarsLLIR + case Throw(exc) => exc.freeVarsLLIR + case Label(label, body, rest) => (body.freeVarsLLIR - label) ++ rest.freeVarsLLIR + case Break(label) => Set(label) + case Continue(label) => Set(label) + case Begin(sub, rest) => sub.freeVarsLLIR ++ rest.freeVarsLLIR + case TryBlock(sub, finallyDo, rest) => sub.freeVarsLLIR ++ finallyDo.freeVarsLLIR ++ rest.freeVarsLLIR + case Assign(lhs, rhs, rest) => Set(lhs) ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR + case AssignField(lhs, nme, rhs, rest) => lhs.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR + case Define(defn, rest) => defn.freeVarsLLIR ++ rest.freeVarsLLIR + case HandleBlock(lhs, res, par, cls, hdr, bod, rst) => + (bod.freeVarsLLIR - lhs) ++ rst.freeVarsLLIR ++ hdr.flatMap(_.freeVars) + case HandleBlockReturn(res) => res.freeVarsLLIR + case End(msg) => Set.empty + lazy val subBlocks: Ls[Block] = this match case Match(p, arms, dflt, rest) => p.subBlocks ++ arms.map(_._2) ++ dflt.toList :+ rest case Begin(sub, rest) => sub :: rest :: Nil @@ -105,7 +131,7 @@ sealed abstract class Block extends Product with AutoLocated: case Throw(r) => r.subBlocks case _: Return | _: Throw | _: Label | _: Break | _: Continue | _: End | _: HandleBlockReturn => Nil - + // Moves definitions in a block to the top. Only scans the top-level definitions of the block; // i.e, definitions inside other definitions are not moved out. Definitions inside `match`/`if` // and `while` statements are moved out. @@ -185,7 +211,7 @@ sealed abstract class Defn: case _: ValDefn => Nil case ClsLikeDefn(preCtor = preCtor, ctor = ctor, methods = mtds) => preCtor :: ctor :: mtds.flatMap(_.subBlocks) - + lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars @@ -195,6 +221,15 @@ sealed abstract class Defn: ++ ctor.freeVars ++ methods.flatMap(_.freeVars) -- privateFields -- publicFields.map(_.sym) -- auxParams.flatMap(_.paramSyms) + lazy val freeVarsLLIR: Set[Local] = this match + case FunDefn(own, sym, params, body) => body.freeVarsLLIR -- params.flatMap(_.paramSyms) - sym + case ValDefn(owner, k, sym, rhs) => rhs.freeVarsLLIR + case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentSym, + methods, privateFields, publicFields, preCtor, ctor) => + preCtor.freeVarsLLIR + ++ ctor.freeVarsLLIR ++ methods.flatMap(_.freeVarsLLIR) + -- privateFields -- publicFields.map(_.sym) -- auxParams.flatMap(_.paramSyms) + final case class FunDefn( owner: Opt[InnerSymbol], sym: BlockMemberSymbol, @@ -233,6 +268,7 @@ final case class Handler( params: Ls[ParamList], body: Block, ): + lazy val freeVarsLLIR: Set[Local] = body.freeVarsLLIR -- params.flatMap(_.paramSyms) - sym - resumeSym lazy val freeVars: Set[Local] = body.freeVars -- params.flatMap(_.paramSyms) - sym - resumeSym /* Represents either unreachable code (for functions that must return a result) @@ -249,6 +285,11 @@ enum Case: case Cls(_, path) => path.freeVars case Tup(_, _) => Set.empty + lazy val freeVarsLLIR: Set[Local] = this match + case Lit(_) => Set.empty + case Cls(_, path) => path.freeVarsLLIR + case Tup(_, _) => Set.empty + sealed abstract class Result: // TODO rm Lam from values and thus the need for this method @@ -259,10 +300,10 @@ sealed abstract class Result: case Value.Lam(params, body) => body :: Nil case Value.Arr(elems) => elems.flatMap(_.value.subBlocks) case _ => Nil - + lazy val freeVars: Set[Local] = this match - case Call(fun, args) => args.flatMap(_.value.freeVars).toSet - case Instantiate(cls, args) => args.flatMap(_.freeVars).toSet + case Call(fun, args) => fun.freeVars ++ args.flatMap(_.value.freeVars).toSet + case Instantiate(cls, args) => cls.freeVars ++ args.flatMap(_.freeVars).toSet case Select(qual, name) => qual.freeVars case Value.Ref(l) => Set(l) case Value.This(sym) => Set.empty @@ -270,6 +311,17 @@ sealed abstract class Result: case Value.Lam(params, body) => body.freeVars -- params.paramSyms case Value.Arr(elems) => elems.flatMap(_.value.freeVars).toSet case DynSelect(qual, fld, arrayIdx) => qual.freeVars ++ fld.freeVars + + lazy val freeVarsLLIR: Set[Local] = this match + case Call(fun, args) => args.flatMap(_.value.freeVarsLLIR).toSet + case Instantiate(cls, args) => args.flatMap(_.freeVarsLLIR).toSet + case Select(qual, name) => qual.freeVarsLLIR + case Value.Ref(l) => Set(l) + case Value.This(sym) => Set.empty + case Value.Lit(lit) => Set.empty + case Value.Lam(params, body) => body.freeVarsLLIR -- params.paramSyms + case Value.Arr(elems) => elems.flatMap(_.value.freeVarsLLIR).toSet + case DynSelect(qual, fld, arrayIdx) => qual.freeVarsLLIR ++ fld.freeVarsLLIR // type Local = LocalSymbol type Local = Symbol diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 615844f36..c4f5a89e7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -15,13 +15,26 @@ import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet +// TODO: modules not working + object Lifter: /** * Describes the free variables of a function that have been accessed by nested definitions. * @param vars The free variables that are accessed by nested classes/functions. * @param reqCapture The free variables that must be captured using a heap-allocated object. */ - case class FreeVars(vars: List[Local], reqCapture: List[Local]) + case class FreeVars(vars: Set[Local], reqCapture: Set[Local]) + + /** + * Describes the free variables of a function that have been accessed by nested definitions. + * @param mp The map from functions' `BlockMemberSymbol`s to their accessed variables. + */ + class UsedLocalsMap(val mp: Map[BlockMemberSymbol, FreeVars]): + def apply(f: BlockMemberSymbol) = mp(f) + private lazy val inverse = mp.flatMap: + case fn -> vars => vars.vars.map(v => v -> fn) + // gets the function to which a local belongs + def lookup(l: Local) = inverse.get(l) def getVars(f: FunDefn): Set[Local] = (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: @@ -32,32 +45,10 @@ object Lifter: * Assumes the input block does not have any `HandleBlock`s. */ class Lifter(using State): - import Lifter.FreeVars + import Lifter.* val varAnalyzer = new UsedVarAnalyzer - // use mutable sets locally to avoid reconstructing everything - // linked hash sets preserve order - private case class FreeVarsMut(vars: LinkedHashSet[Local], mutated: LinkedHashSet[Local]): - def toImmut = FreeVars(vars.toList, mutated.toList) - - /** - * Describes the free variables of a function that have been accessed by nested definitions. - * @param mp The map from functions' `BlockMemberSymbol`s to their accessed variables. - */ - class UsedLocalsMap(val mp: Map[BlockMemberSymbol, FreeVars]): - def apply(f: BlockMemberSymbol) = mp(f) - private lazy val inverse = mp.flatMap: - case fn -> vars => vars.vars.map(v => v -> fn) - // gets the function to which a local belongs - def lookup(l: Local) = inverse.get(l) - - object UsedLocalsMap: - def from(mp: Map[BlockMemberSymbol, FreeVars]) = - UsedLocalsMap(mp.map: - case a -> b => a -> b - ) - /** * The context of the class lifter. * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested defns @@ -109,21 +100,6 @@ class Lifter(using State): def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) - - /** - * Finds the used locals of functions which have been used by their nested definitions. - * - * @param b - * @return - */ - def findUsedLocals(b: Block): UsedLocalsMap = - var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - val walker = new BlockTransformer(SymbolSubst()): - override def applyFunDefn(fun: FunDefn): FunDefn = - usedMap += (fun.sym -> varAnalyzer.reqdCaptureLocals(fun)) - super.applyFunDefn(fun) - walker.applyBlock(b) - UsedLocalsMap.from(usedMap) /** * Creates a capture class for a function consisting of its mutable (and possibly immutable) local variables. @@ -382,7 +358,8 @@ class Lifter(using State): case Some(value) if !belongsToCtor(value) => Select(value.asPath, t.id)(N) case _ => super.applyPath(p) case Value.Ref(l) => ctx.getLocalCaptureSym(l) match - case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) + case Some(captureSym) => + Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) case None => ctx.getLocalPath(l) match case Some(value) => Value.Ref(value) case None => super.applyPath(p) @@ -650,7 +627,7 @@ class Lifter(using State): // top-level def transform(b: Block) = val blk = desugarLambdas(b) - val ctx = LifterCtx.withLocals(findUsedLocals(blk)) + val ctx = LifterCtx.withLocals(varAnalyzer.findUsedLocals(blk)) val ctxx = ctx.addBmsReqdInfo(createLiftInfo(blk, ctx)) val walker = new BlockTransformerShallow(SymbolSubst()): @@ -678,7 +655,7 @@ class UsedVarAnalyzer: // which reduces the number of passes over the tree by one private val usedVarsCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty - def getUsedVars(f: FunDefn): Set[Local] = usedVarsCache.get(f.sym) match + private def getUsedVars(f: FunDefn): Set[Local] = usedVarsCache.get(f.sym) match case Some(value) => value case None => val fVars = Lifter.getVars(f) @@ -730,18 +707,22 @@ class UsedVarAnalyzer: * @param defn The definition to search through * @return The variables which this definition could possibly mutate. */ - def findMutations(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match + private def findMutations(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match case Some(value) => value - case None => defn match - case f: FunDefn => findBlkMutations(f.body) - case c: ClsLikeDefn => - findBlkMutations(c.preCtor) ++ findBlkMutations(c.ctor) ++ c.methods.flatMap(findMutations) - case _: ValDefn => Set.empty + case None => + val ret = defn match + case f: FunDefn => + findBlkMutations(f.body) + case c: ClsLikeDefn => + findBlkMutations(c.preCtor) ++ findBlkMutations(c.ctor) ++ c.methods.flatMap(findMutations) + case _: ValDefn => Set.empty + mutatedCache.addOne(defn.sym -> ret) + ret // 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. - def reqdCaptureLocals(f: FunDefn) = + private def reqdCaptureLocals(f: FunDefn) = var (_, defns) = f.body.floatOutDefns val defnSyms = defns.collect: case f: FunDefn => f.sym -> f @@ -804,6 +785,11 @@ class UsedVarAnalyzer: rec(finallyDo) |> merge applyBlock(rest) b + case Return(res, false) => + applyResult(res) + hasReader = Set.empty + hasMutator = Set.empty + b case _ => super.applyBlock(b) override def applyValue(v: Value): Value = v match @@ -834,4 +820,50 @@ class UsedVarAnalyzer: val reqCapture = go(f.body, Set.empty, Set.empty, Set.empty).reqCapture val usedVars = defns.flatMap(_.freeVars.intersect(thisVars)).toSet - Lifter.FreeVars(usedVars.toList, reqCapture.toList) \ No newline at end of file + (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 + def findUsedLocalsImpl(f: FunDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = + val thisVars = Lifter.getVars(f) -- existing + val newExisting = existing ++ thisVars + + val (vars, cap) = reqdCaptureLocals(f) + + var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty + usedMap += (f.sym -> Lifter.FreeVars(vars.intersect(thisVars), cap.intersect(thisVars))) + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = defn match + case f: FunDefn => + usedMap ++= findUsedLocalsImpl(f, newExisting) + f + case c: ClsLikeDefn => + val newNewExisting = newExisting ++ c.preCtor.definedVars ++ c.ctor.definedVars + for f <- c.methods do + usedMap ++= findUsedLocalsImpl(f, newNewExisting) + c + case d => super.applyDefn(d) + walker.applyBlock(f.body) + usedMap + + /** + * Finds the used locals of functions which have been used by their nested definitions. + * + * @param b + * @return + */ + def findUsedLocals(b: Block): Lifter.UsedLocalsMap = + var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = defn match + case f: FunDefn => + usedMap ++= findUsedLocalsImpl(f, b.definedVars) + f + case c: ClsLikeDefn => + val newNewExisting = b.definedVars ++ c.preCtor.definedVars ++ c.ctor.definedVars + for f <- c.methods do + usedMap ++= findUsedLocalsImpl(f, newNewExisting) + c + case d => super.applyDefn(d) + walker.applyBlock(b) + Lifter.UsedLocalsMap(usedMap) \ No newline at end of file diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala index 42268459e..ef8087e72 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -235,7 +235,7 @@ final class LlirBuilder(tl: TraceLogger)(fresh: Fresh, fnUid: FreshInt, clsUid: bPath(scrut): case e: TrivialExpr => val jp = fresh.make("j") - val fvset = (rest.freeVars -- rest.definedVars).map(allocIfNew) + val fvset = (rest.freeVarsLLIR -- rest.definedVars).map(allocIfNew) val fvs1 = fvset.toList val new_ctx = fvs1.foldLeft(ctx)((acc, x) => acc.addName(x, fresh.make)) val fvs = fvs1.map(new_ctx.findName(_)) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index bf09024da..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,75 +7,5 @@ :global :d -:sjs -:lift -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - Test(unused1) -f(1, 2).get() -//│ Elab: { ‹› fun member:f‹179›(Param(‹›,used1‹180›,None), Param(‹›,unused1‹181›,None), ) = { ‹› fun member:g‹183›(Param(‹›,g_arg‹184›,None), ) = { ‹› fun member:h‹185› = used3‹186›#666; let used3‹186›; used3‹186› = 2; builtin:+‹16›#0(used1‹180›#666, member:h‹185›#666) }; let unused2‹190›; unused2‹190› = 2; Cls TestParamList(‹›,List(Param(‹›,class:Test‹191›.a,None)),None) { ‹› fun member:get‹176›() = used1‹180›#666; }; member:Test‹182›#666(unused1‹181›#666) }; member:f‹179›#666(1, 2).get() } -//│ JS (unsanitized): -//│ let f, Test1, g, h, tmp, Test$ctor, Test$, g$, h$; -//│ h$ = function h$(used3, used1) { -//│ return used3; -//│ }; -//│ h = function h(used3, used1) { -//│ return () => { -//│ return h$(used3, used1); -//│ }; -//│ }; -//│ g$ = function g$(used1, g_arg) { -//│ let used3, tmp1; -//│ used3 = 2; -//│ tmp1 = h$(used3, used1); -//│ return used1 + tmp1; -//│ }; -//│ g = function g(used1) { -//│ return (g_arg) => { -//│ return g$(used1, g_arg); -//│ }; -//│ }; -//│ Test$ = function Test$(used1, a) { -//│ let tmp1; -//│ tmp1 = new Test1(a); -//│ return tmp1(used1); -//│ }; -//│ Test$ctor = function Test$ctor(used1) { -//│ return (a) => { -//│ let tmp1; -//│ tmp1 = new Test1(a); -//│ return tmp1(used1); -//│ }; -//│ }; -//│ Test1 = function Test(a1) { -//│ return (used11) => { -//│ return new Test.class(a1)(used11); -//│ } -//│ }; -//│ Test1.class = class Test { -//│ constructor(a) { -//│ return (used1) => { -//│ this.a = a; -//│ this.used1 = used1; -//│ return this; -//│ } -//│ } -//│ get() { -//│ return this.used1; -//│ } -//│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } -//│ }; -//│ f = function f(used1, unused1) { -//│ let unused2; -//│ unused2 = 2; -//│ return Test$(used1, unused1); -//│ }; -//│ tmp = f(1, 2); -//│ tmp.get() ?? null -//│ = 1 + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index b5bf24c7e..d7b447754 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -436,7 +436,6 @@ g() // make sure class fields are discriminated properly for immutable captures when supported :handler -:fixme abstract class Effect with fun perform() handle h = Effect with @@ -445,7 +444,7 @@ fun f() = h.perform() 1 f() + f() + f() -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: h (class hkmc2.semantics.VarSymbol) +//│ = 3 /// CLASSES /// @@ -639,7 +638,6 @@ A(2).f() //│ ═══[RUNTIME ERROR] Expected: 2, got: null :sjs -:fixme fun g() = let y = 0 fun f(x) = @@ -654,7 +652,47 @@ fun g() = x f g()(1) -//│ /!!!\ Uncaught error: java.util.NoSuchElementException: None.get +//│ JS (unsanitized): +//│ let g20, f24, h8, tmp29, f$3, h$7, g$capture1; +//│ h$7 = function h$(k, x1, g$capture2) { +//│ k = 5; +//│ x1 = 4; +//│ return x1 + g$capture2.y0$; +//│ }; +//│ h8 = function h(k, x1, g$capture2) { +//│ return () => { +//│ return h$7(k, x1, g$capture2); +//│ }; +//│ }; +//│ f$3 = function f$(g$capture2, x1) { +//│ let k; +//│ k = 4; +//│ g$capture2.y0$ = 2; +//│ return x1; +//│ }; +//│ f24 = function f(g$capture2) { +//│ return (x1) => { +//│ return f$3(g$capture2, x1); +//│ }; +//│ }; +//│ g$capture1 = function g$capture(y0$1) { +//│ return new g$capture.class(y0$1); +//│ }; +//│ g$capture1.class = class g$capture { +//│ constructor(y0$) { +//│ this.y0$ = y0$; +//│ } +//│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } +//│ }; +//│ g20 = function g() { +//│ let capture; +//│ capture = new g$capture1(null); +//│ capture.y0$ = 0; +//│ return f24(capture) ?? null; +//│ }; +//│ tmp29 = g20(); +//│ tmp29(1) ?? null +//│ = 1 // only w should be in a capture :expect 10111 @@ -675,17 +713,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let f24, Bad1, Good1, tmp29, Bad$ctor, Bad$, Good$ctor, Good$, f$capture19; +//│ let f25, Bad1, Good1, tmp30, Bad$ctor, Bad$, Good$ctor, Good$, f$capture19; //│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { -//│ let tmp30; -//│ tmp30 = new Good1(); -//│ return tmp30(x$1, y$2, z$3, f$capture$0); +//│ let tmp31; +//│ tmp31 = new Good1(); +//│ return tmp31(x$1, y$2, z$3, f$capture$0); //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { -//│ let tmp30; -//│ tmp30 = new Good1(); -//│ return tmp30(x$1, y$2, z$3, f$capture$0); +//│ let tmp31; +//│ tmp31 = new Good1(); +//│ return tmp31(x$1, y$2, z$3, f$capture$0); //│ }; //│ }; //│ Good1 = function Good() { @@ -704,24 +742,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp30, tmp31; +//│ let tmp31, tmp32; //│ this.z$3 = 100; -//│ tmp30 = this.x$1 + this.y$2; -//│ tmp31 = tmp30 + this.z$3; -//│ return tmp31 + this.f$capture$0.w0$; +//│ tmp31 = this.x$1 + this.y$2; +//│ tmp32 = tmp31 + this.z$3; +//│ return tmp32 + this.f$capture$0.w0$; //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(x$1, y$2, z$3, f$capture$0) { -//│ let tmp30; -//│ tmp30 = new Bad1(); -//│ return tmp30(x$1, y$2, z$3, f$capture$0); +//│ let tmp31; +//│ tmp31 = new Bad1(); +//│ return tmp31(x$1, y$2, z$3, f$capture$0); //│ }; //│ Bad$ctor = function Bad$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { -//│ let tmp30; -//│ tmp30 = new Bad1(); -//│ return tmp30(x$1, y$2, z$3, f$capture$0); +//│ let tmp31; +//│ tmp31 = new Bad1(); +//│ return tmp31(x$1, y$2, z$3, f$capture$0); //│ }; //│ }; //│ Bad1 = function Bad() { @@ -754,19 +792,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } //│ }; -//│ f24 = function f() { -//│ let x1, y1, z, tmp30, tmp31, capture; +//│ f25 = function f() { +//│ let x1, y1, z, tmp31, tmp32, capture; //│ capture = new f$capture19(null); //│ x1 = 1; //│ y1 = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp30 = Bad$(x1, y1, z, capture); -//│ tmp31 = tmp30.foo() ?? null; +//│ tmp31 = Bad$(x1, y1, z, capture); +//│ tmp32 = tmp31.foo() ?? null; //│ return Good$(x1, y1, z, capture); //│ }; -//│ tmp29 = f24(); -//│ tmp29.foo() ?? null +//│ tmp30 = f25(); +//│ tmp30.foo() ?? null //│ = 10111 :expect 2 diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index f2b1cb688..fe4f02155 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -358,6 +358,7 @@ handle h = Eff with foo(h) //│ = 123 +:sjs :expect 123 fun foo(h) = h.perform() @@ -382,4 +383,253 @@ fun foo(h) = handle h = Eff with fun perform()(k) = k(()) foo(h) +//│ JS (unsanitized): +//│ let tmp19, handleBlock$19; +//│ handleBlock$19 = function handleBlock$() { +//│ let foo1, h, res2, Cont$40, Handler$h$21; +//│ Handler$h$21 = function Handler$h$() { +//│ return new Handler$h$.class(); +//│ }; +//│ Handler$h$21.class = class Handler$h$20 extends Eff1 { +//│ constructor() { +//│ let tmp20; +//│ tmp20 = super(); +//│ } +//│ perform() { +//│ return globalThis.Predef.__mkEffect(this, (k) => { +//│ return k(null) ?? null; +//│ }); +//│ } +//│ toString() { return "Handler$h$(" + "" + ")"; } +//│ }; +//│ h = Handler$h$21(); +//│ Cont$40 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$40.class = class Cont$38 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp20; +//│ tmp20 = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 8) { +//│ res2 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 8) { +//│ return res2; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ foo1 = function foo(h1) { +//│ let A8, a, A9, scrut, A10, b, A11, scrut1, A12, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, res3, res4, res5, res6, res7, res8, Cont$41; +//│ Cont$41 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$41.class = class Cont$39 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp27; +//│ tmp27 = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ res3 = value$; +//│ } else if (this.pc === 2) { +//│ res5 = value$; +//│ } else if (this.pc === 1) { +//│ res4 = value$; +//│ } else if (this.pc === 4) { +//│ res7 = value$; +//│ } else if (this.pc === 3) { +//│ res6 = value$; +//│ } else if (this.pc === 5) { +//│ res8 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ tmp20 = res3; +//│ scrut = true; +//│ if (scrut === true) { +//│ res4 = A10.f(); +//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 1; +//│ return globalThis.Predef.__appendInCont(res4, this); +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else { +//│ res5 = A9.f(); +//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 2; +//│ return globalThis.Predef.__appendInCont(res5, this); +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 7) { +//│ a = tmp21; +//│ scrut1 = false; +//│ if (scrut1 === true) { +//│ res6 = A12.f(); +//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 3; +//│ return globalThis.Predef.__appendInCont(res6, this); +//│ } +//│ this.pc = 3; +//│ continue contLoop; +//│ } else { +//│ res7 = A11.f(); +//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 4; +//│ return globalThis.Predef.__appendInCont(res7, this); +//│ } +//│ this.pc = 4; +//│ continue contLoop; +//│ } +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ tmp21 = res5; +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ tmp21 = res4; +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ b = tmp22; +//│ res8 = A8(); +//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 5; +//│ return globalThis.Predef.__appendInCont(res8, this); +//│ } +//│ this.pc = 5; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ tmp22 = res7; +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ tmp22 = res6; +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 5) { +//│ tmp23 = res8; +//│ tmp24 = tmp23 * 100; +//│ tmp25 = a * 10; +//│ tmp26 = tmp24 + tmp25; +//│ return tmp26 + b; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ A8 = function A() { +//│ return 1; +//│ }; +//│ A10 = class A4 { +//│ static {} +//│ static f() { +//│ return 2; +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A9 = class A5 { +//│ static {} +//│ static f() { +//│ return 3; +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A12 = class A6 { +//│ static {} +//│ static f() { +//│ return 2; +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A11 = class A7 { +//│ static {} +//│ static f() { +//│ return 3; +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ res3 = h1.perform() ?? null; +//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { +//│ res3.tail.next = Cont$41(0); +//│ res3.tail = res3.tail.next; +//│ return res3; +//│ } +//│ tmp20 = res3; +//│ scrut = true; +//│ if (scrut === true) { +//│ res4 = A10.f(); +//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { +//│ res4.tail.next = Cont$41(1); +//│ res4.tail = res4.tail.next; +//│ return res4; +//│ } +//│ tmp21 = res4; +//│ } else { +//│ res5 = A9.f(); +//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { +//│ res5.tail.next = Cont$41(2); +//│ res5.tail = res5.tail.next; +//│ return res5; +//│ } +//│ tmp21 = res5; +//│ } +//│ a = tmp21; +//│ scrut1 = false; +//│ if (scrut1 === true) { +//│ res6 = A12.f(); +//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { +//│ res6.tail.next = Cont$41(3); +//│ res6.tail = res6.tail.next; +//│ return res6; +//│ } +//│ tmp22 = res6; +//│ } else { +//│ res7 = A11.f(); +//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { +//│ res7.tail.next = Cont$41(4); +//│ res7.tail = res7.tail.next; +//│ return res7; +//│ } +//│ tmp22 = res7; +//│ } +//│ b = tmp22; +//│ res8 = A8(); +//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { +//│ res8.tail.next = Cont$41(5); +//│ res8.tail = res8.tail.next; +//│ return res8; +//│ } +//│ tmp23 = res8; +//│ tmp24 = tmp23 * 100; +//│ tmp25 = a * 10; +//│ tmp26 = tmp24 + tmp25; +//│ return tmp26 + b; +//│ }; +//│ res2 = foo1(h); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$40(8); +//│ return globalThis.Predef.__handleBlockImpl(res2, h); +//│ } +//│ return res2; +//│ }; +//│ tmp19 = handleBlock$19(); +//│ if (tmp19 instanceof this.Predef.__EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ tmp19 //│ = 123 diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 520adcbd7..4b42be534 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -1,4 +1,5 @@ :js +:lift // sanity check :expect 5050 @@ -20,58 +21,90 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, res, handleBlock$; -//│ hi = function hi(n) { -//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, res1, Cont$3; -//│ Cont$3 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, hi$capture1, handleBlock$$capture1, lambda$capture1; +//│ Cont$$ = function Cont$$(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$3(pc); +//│ return tmp(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0); +//│ }; +//│ Cont$$ctor = function Cont$$ctor(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$3(pc); +//│ return tmp(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0); //│ }; -//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp1; -//│ tmp1 = super(null, null); +//│ }; +//│ Cont$3 = function Cont$(pc1) { +//│ return (scrut$11, tmp$21, dummy$31, n$41, hi$capture$01) => { +//│ return new Cont$.class(pc1)(scrut$11, tmp$21, dummy$31, n$41, hi$capture$01); +//│ } +//│ }; +//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (scrut$1, tmp$2, dummy$3, n$4, hi$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); //│ this.pc = pc; +//│ this.scrut$1 = scrut$1; +//│ this.tmp$2 = tmp$2; +//│ this.dummy$3 = dummy$3; +//│ this.n$4 = n$4; +//│ this.hi$capture$0 = hi$capture$0; +//│ return this; //│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ res1 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 2) { -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0; -//│ } else { -//│ tmp = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi(tmp); -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ break contLoop; -//│ } else if (this.pc === 0) { -//│ dummy = res1; -//│ this.pc = 2; -//│ continue contLoop; +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.hi$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 2) { +//│ this.scrut$1 = this.n$4 == 0; +//│ if (this.scrut$1 === true) { +//│ return 0; +//│ } else { +//│ this.tmp$2 = this.n$4 - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ return hi(this.tmp$2); //│ } -//│ break; +//│ this.pc = 1; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ break contLoop; +//│ } else if (this.pc === 0) { +//│ this.dummy$3 = this.hi$capture$0.res0$; +//│ this.pc = 2; +//│ continue contLoop; //│ } +//│ break; //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ hi$capture1 = function hi$capture(res0$1) { +//│ return new hi$capture.class(res0$1); +//│ }; +//│ hi$capture1.class = class hi$capture { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "hi$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ hi = function hi(n) { +//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, capture; +//│ capture = new hi$capture1(null); //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; //│ handlerExists = globalThis.Predef.__stackHandler !== undefined; //│ scrut1 = diffGeqLimit && handlerExists; //│ if (scrut1 === true) { -//│ res1 = globalThis.Predef.__stackHandler.perform(); -//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$3(0); -//│ res1.tail = res1.tail.next; -//│ return res1; +//│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$(scrut, tmp, dummy, n, capture, 0); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$; //│ } -//│ dummy = res1; +//│ dummy = capture.res0$; //│ } //│ scrut = n == 0; //│ if (scrut === true) { @@ -82,88 +115,173 @@ hi(0) //│ return hi(tmp); //│ } //│ }; -//│ handleBlock$ = function handleBlock$() { -//│ let stackHandler, res1, Cont$3, StackDelay$1; -//│ StackDelay$1 = function StackDelay$() { -//│ return new StackDelay$.class(); +//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { +//│ let tmp; +//│ tmp = new StackDelay$1(); +//│ return tmp(handleBlock$$capture$0); +//│ }; +//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { +//│ return () => { +//│ let tmp; +//│ tmp = new StackDelay$1(); +//│ return tmp(handleBlock$$capture$0); //│ }; -//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { -//│ constructor() { +//│ }; +//│ Cont$$2 = function Cont$$(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1, pc) { +//│ let tmp; +//│ tmp = new Cont$5(pc); +//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ }; +//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$5(pc); +//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ }; +//│ }; +//│ Cont$5 = function Cont$(pc1) { +//│ return (StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11); +//│ } +//│ }; +//│ Cont$5.class = class Cont$1 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) => { //│ let tmp; -//│ tmp = super(); +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.StackDelay$$instance$3 = StackDelay$$instance$3; +//│ this.res$2 = res$2; +//│ this.lambda$capture$0 = lambda$capture$0; +//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; +//│ return this; //│ } -//│ perform() { -//│ return globalThis.Predef.__mkEffect(this, (resume) => { -//│ let res2, res3, Cont$4; -//│ Cont$4 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 4) { -//│ res3 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ res2 = res3; -//│ return res2; -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ res3 = resume(); -//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$4(4); -//│ res3.tail = res3.tail.next; -//│ return res3; -//│ } -//│ res2 = res3; -//│ return res2; -//│ }); +//│ } +//│ resume(value$) { +//│ if (this.pc === 4) { +//│ this.lambda$capture$0.res0$ = value$; //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ this.res$2 = this.lambda$capture$0.res0$; +//│ return this.res$2; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ lambda$capture1 = function lambda$capture(res0$1) { +//│ return new lambda$capture.class(res0$1); +//│ }; +//│ lambda$capture1.class = class lambda$capture { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ lambda$ = function lambda$(StackDelay$$instance, handleBlock$$capture2, resume) { +//│ let res1, capture; +//│ capture = new lambda$capture1(null); +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ capture.res0$ = resume(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$2(StackDelay$$instance, res1, capture, handleBlock$$capture2, 4); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$; +//│ } +//│ res1 = capture.res0$; +//│ return res1; +//│ }; +//│ lambda = function lambda(StackDelay$$instance, handleBlock$$capture2) { +//│ return (resume) => { +//│ return lambda$(StackDelay$$instance, handleBlock$$capture2, resume); //│ }; -//│ stackHandler = StackDelay$1(); -//│ Cont$3 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ }; +//│ StackDelay$1 = function StackDelay$() { +//│ return (handleBlock$$capture$01) => { +//│ return new StackDelay$.class()(handleBlock$$capture$01); +//│ } +//│ }; +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ constructor() { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(); +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = lambda(this, this.handleBlock$$capture$0) ?? null; +//│ return globalThis.Predef.__mkEffect(this, lambda$this); +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ Cont$$1 = function Cont$$(handleBlock$$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$4(pc); +//│ return tmp(handleBlock$$capture$0); +//│ }; +//│ Cont$$ctor1 = function Cont$$ctor(handleBlock$$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$4(pc); +//│ return tmp(handleBlock$$capture$0); //│ }; -//│ Cont$3.class = class Cont$2 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { +//│ }; +//│ Cont$4 = function Cont$(pc1) { +//│ return (handleBlock$$capture$01) => { +//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ } +//│ }; +//│ Cont$4.class = class Cont$2 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (handleBlock$$capture$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; //│ } -//│ resume(value$) { +//│ } +//│ resume(value$) { +//│ if (this.pc === 3) { +//│ this.handleBlock$$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ res1 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 3) { -//│ return res1; -//│ } -//│ break; +//│ return this.handleBlock$$capture$0.res0$; //│ } +//│ break; //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$$capture1 = function handleBlock$$capture(res0$1) { +//│ return new handleBlock$$capture.class(res0$1); +//│ }; +//│ handleBlock$$capture1.class = class handleBlock$$capture { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let stackHandler, capture; +//│ capture = new handleBlock$$capture1(null); +//│ stackHandler = StackDelay$$(capture); //│ globalThis.Predef.__stackLimit = 5; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ res1 = hi(0); -//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$3(3); -//│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler); +//│ capture.res0$ = hi(0); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$1(capture, 3); +//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, stackHandler); //│ } -//│ return res1; +//│ return capture.res0$; //│ }; //│ res = handleBlock$(); //│ if (res instanceof this.Predef.__EffectSig.class) { @@ -184,81 +302,118 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, handleBlock$1; -//│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$6; -//│ Cont$6 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, sum$capture1, handleBlock$$capture3, lambda$capture3; +//│ Cont$$3 = function Cont$$(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$9(pc); +//│ return tmp(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0); +//│ }; +//│ Cont$$ctor3 = function Cont$$ctor(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$9(pc); +//│ return tmp(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0); //│ }; -//│ Cont$6.class = class Cont$3 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp3; -//│ tmp3 = super(null, null); +//│ }; +//│ Cont$9 = function Cont$(pc1) { +//│ return (offsetGtDepth$11, n$21, tmp$31, prevDepth$41, tmp$51, scrut$61, dummy$71, tmp$81, sum$capture$01) => { +//│ return new Cont$.class(pc1)(offsetGtDepth$11, n$21, tmp$31, prevDepth$41, tmp$51, scrut$61, dummy$71, tmp$81, sum$capture$01); +//│ } +//│ }; +//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); //│ this.pc = pc; +//│ this.offsetGtDepth$1 = offsetGtDepth$1; +//│ this.n$2 = n$2; +//│ this.tmp$3 = tmp$3; +//│ this.prevDepth$4 = prevDepth$4; +//│ this.tmp$5 = tmp$5; +//│ this.scrut$6 = scrut$6; +//│ this.dummy$7 = dummy$7; +//│ this.tmp$8 = tmp$8; +//│ this.sum$capture$0 = sum$capture$0; +//│ return this; //│ } -//│ resume(value$) { -//│ if (this.pc === 1) { -//│ res3 = value$; -//│ } else if (this.pc === 0) { -//│ res2 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0; -//│ } else { -//│ tmp = n - 1; -//│ prevDepth = globalThis.Predef.__stackDepth; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ res3 = sum1(tmp); -//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(res3, this); -//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 1) { +//│ this.sum$capture$0.res1$ = value$; +//│ } else if (this.pc === 0) { +//│ this.sum$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ this.scrut$6 = this.n$2 == 0; +//│ if (this.scrut$6 === true) { +//│ return 0; +//│ } else { +//│ this.tmp$3 = this.n$2 - 1; +//│ this.prevDepth$4 = globalThis.Predef.__stackDepth; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ this.sum$capture$0.res1$ = sum1(this.tmp$3); +//│ if (this.sum$capture$0.res1$ instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ continue contLoop; +//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.res1$, this); //│ } -//│ this.pc = 2; +//│ this.pc = 1; //│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ break contLoop; -//│ } else if (this.pc === 1) { -//│ tmp2 = res3; -//│ globalThis.Predef.__stackDepth = prevDepth; -//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; -//│ if (offsetGtDepth === true) { -//│ globalThis.Predef.__stackOffset = prevDepth; -//│ this.pc = 3; -//│ continue contLoop; -//│ } +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 1) { +//│ this.tmp$8 = this.sum$capture$0.res1$; +//│ globalThis.Predef.__stackDepth = this.prevDepth$4; +//│ this.offsetGtDepth$1 = this.prevDepth$4 < globalThis.Predef.__stackOffset; +//│ if (this.offsetGtDepth$1 === true) { +//│ globalThis.Predef.__stackOffset = this.prevDepth$4; //│ this.pc = 3; //│ continue contLoop; -//│ } else if (this.pc === 3) { -//│ tmp1 = tmp2; -//│ return n + tmp1; -//│ } else if (this.pc === 0) { -//│ dummy = res2; -//│ this.pc = 4; -//│ continue contLoop; //│ } -//│ break; +//│ this.pc = 3; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ this.tmp$5 = this.tmp$8; +//│ return this.n$2 + this.tmp$5; +//│ } else if (this.pc === 0) { +//│ this.dummy$7 = this.sum$capture$0.res0$; +//│ this.pc = 4; +//│ continue contLoop; //│ } +//│ break; //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ sum$capture1 = function sum$capture(res0$1, res1$1) { +//│ return new sum$capture.class(res0$1, res1$1); +//│ }; +//│ sum$capture1.class = class sum$capture { +//│ constructor(res0$, res1$) { +//│ this.res0$ = res0$; +//│ this.res1$ = res1$; +//│ } +//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.res0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ }; +//│ sum1 = function sum(n) { +//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, capture; +//│ capture = new sum$capture1(null, null); //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; //│ handlerExists = globalThis.Predef.__stackHandler !== undefined; //│ scrut1 = diffGeqLimit && handlerExists; //│ if (scrut1 === true) { -//│ res2 = globalThis.Predef.__stackHandler.perform(); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$6(0); -//│ res2.tail = res2.tail.next; -//│ return res2; +//│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$3(offsetGtDepth, n, tmp, prevDepth, tmp1, scrut, dummy, tmp2, capture, 0); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$; //│ } -//│ dummy = res2; +//│ dummy = capture.res0$; //│ } //│ scrut = n == 0; //│ if (scrut === true) { @@ -267,13 +422,13 @@ sum(10000) //│ tmp = n - 1; //│ prevDepth = globalThis.Predef.__stackDepth; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ res3 = sum1(tmp); -//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$6(1); -//│ res3.tail = res3.tail.next; -//│ return res3; +//│ capture.res1$ = sum1(tmp); +//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res1$.tail.next = Cont$$3(offsetGtDepth, n, tmp, prevDepth, tmp1, scrut, dummy, tmp2, capture, 1); +//│ capture.res1$.tail = capture.res1$.tail.next; +//│ return capture.res1$; //│ } -//│ tmp2 = res3; +//│ tmp2 = capture.res1$; //│ globalThis.Predef.__stackDepth = prevDepth; //│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; //│ if (offsetGtDepth === true) { @@ -283,88 +438,173 @@ sum(10000) //│ return n + tmp1; //│ } //│ }; -//│ handleBlock$1 = function handleBlock$() { -//│ let stackHandler, res2, Cont$6, StackDelay$2; -//│ StackDelay$2 = function StackDelay$() { -//│ return new StackDelay$.class(); +//│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { +//│ let tmp; +//│ tmp = new StackDelay$3(); +//│ return tmp(handleBlock$$capture$0); +//│ }; +//│ StackDelay$$ctor1 = function StackDelay$$ctor(handleBlock$$capture$0) { +//│ return () => { +//│ let tmp; +//│ tmp = new StackDelay$3(); +//│ return tmp(handleBlock$$capture$0); //│ }; -//│ StackDelay$2.class = class StackDelay$1 extends globalThis.Predef.__StackDelay.class { -//│ constructor() { +//│ }; +//│ Cont$$5 = function Cont$$(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1, pc) { +//│ let tmp; +//│ tmp = new Cont$11(pc); +//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ }; +//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$11(pc); +//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ }; +//│ }; +//│ Cont$11 = function Cont$(pc1) { +//│ return (StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11); +//│ } +//│ }; +//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) => { //│ let tmp; -//│ tmp = super(); +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.StackDelay$$instance$3 = StackDelay$$instance$3; +//│ this.res$2 = res$2; +//│ this.lambda$capture$0 = lambda$capture$0; +//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; +//│ return this; //│ } -//│ perform() { -//│ return globalThis.Predef.__mkEffect(this, (resume) => { -//│ let res3, res4, Cont$7; -//│ Cont$7 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$7.class = class Cont$4 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 6) { -//│ res4 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 6) { -//│ res3 = res4; -//│ return res3; -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ res4 = resume(); -//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = Cont$7(6); -//│ res4.tail = res4.tail.next; -//│ return res4; -//│ } -//│ res3 = res4; -//│ return res3; -//│ }); +//│ } +//│ resume(value$) { +//│ if (this.pc === 6) { +//│ this.lambda$capture$0.res0$ = value$; //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ contLoop: while (true) { +//│ if (this.pc === 6) { +//│ this.res$2 = this.lambda$capture$0.res0$; +//│ return this.res$2; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ lambda$capture3 = function lambda$capture(res0$1) { +//│ return new lambda$capture.class(res0$1); +//│ }; +//│ lambda$capture3.class = class lambda$capture2 { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ lambda$1 = function lambda$(StackDelay$$instance, handleBlock$$capture4, resume) { +//│ let res2, capture; +//│ capture = new lambda$capture3(null); +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ capture.res0$ = resume(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, res2, capture, handleBlock$$capture4, 6); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$; +//│ } +//│ res2 = capture.res0$; +//│ return res2; +//│ }; +//│ lambda1 = function lambda(StackDelay$$instance, handleBlock$$capture4) { +//│ return (resume) => { +//│ return lambda$1(StackDelay$$instance, handleBlock$$capture4, resume); //│ }; -//│ stackHandler = StackDelay$2(); -//│ Cont$6 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ }; +//│ StackDelay$3 = function StackDelay$() { +//│ return (handleBlock$$capture$01) => { +//│ return new StackDelay$.class()(handleBlock$$capture$01); +//│ } +//│ }; +//│ StackDelay$3.class = class StackDelay$2 extends globalThis.Predef.__StackDelay.class { +//│ constructor() { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(); +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = lambda1(this, this.handleBlock$$capture$0) ?? null; +//│ return globalThis.Predef.__mkEffect(this, lambda$this); +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$10(pc); +//│ return tmp(handleBlock$$capture$0); +//│ }; +//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$10(pc); +//│ return tmp(handleBlock$$capture$0); //│ }; -//│ Cont$6.class = class Cont$5 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { +//│ }; +//│ Cont$10 = function Cont$(pc1) { +//│ return (handleBlock$$capture$01) => { +//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ } +//│ }; +//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (handleBlock$$capture$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; //│ } -//│ resume(value$) { +//│ } +//│ resume(value$) { +//│ if (this.pc === 5) { +//│ this.handleBlock$$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { //│ if (this.pc === 5) { -//│ res2 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 5) { -//│ return res2; -//│ } -//│ break; +//│ return this.handleBlock$$capture$0.res0$; //│ } +//│ break; //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$$capture3 = function handleBlock$$capture(res0$1) { +//│ return new handleBlock$$capture.class(res0$1); +//│ }; +//│ handleBlock$$capture3.class = class handleBlock$$capture2 { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ handleBlock$1 = function handleBlock$() { +//│ let stackHandler, capture; +//│ capture = new handleBlock$$capture3(null); +//│ stackHandler = StackDelay$$1(capture); //│ globalThis.Predef.__stackLimit = 1000; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ res2 = sum1(10000); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$6(5); -//│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler); +//│ capture.res0$ = sum1(10000); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$4(capture, 5); +//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, stackHandler); //│ } -//│ return res2; +//│ return capture.res0$; //│ }; //│ res1 = handleBlock$1(); //│ if (res1 instanceof this.Predef.__EffectSig.class) { @@ -411,7 +651,8 @@ foo :re fun foo() = - val f = n => + let f = () + set f = n => if n <= 0 then 0 else n + f(n-1) f(10000) @@ -422,13 +663,16 @@ abstract class Eff with fun perform(a): () // functions and lambdas inside handlers +// note: changed to a `let` binding because we plan to disallow val inside functions anyway, +// and class lifting breaks it :handler :stackSafe 100 :expect 50005000 fun foo(h) = h.perform handle h = Eff with fun perform(resume) = - val f = n => + let f = () + set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) @@ -441,7 +685,8 @@ foo(h) :expect 50005000 handle h = Eff with fun perform(resume) = - val f = n => + let f = () + set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) @@ -455,7 +700,8 @@ in fun foo(h) = h.perform(2) handle h = Eff with fun perform(a)(resume) = - val f = n => + let f = () + set f = n => if n <= 0 then 0 else n + f(n-1) resume(f(10000)) From 6f2350ac750868b614f39fdee10bf2b2b6c50a45 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 2 Feb 2025 01:02:34 +0800 Subject: [PATCH 039/127] Add broken test --- .../src/test/mlscript/codegen/Lifter.mls | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index d7b447754..e30cb2460 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -819,3 +819,60 @@ ret() ret() //│ = 2 //│ ret = [Function (anonymous)] + +:expect 6 +:fixme +:lift +fun f(x, y, z) = + fun g() = + set x = 1 + h() + x + y + z + fun h() = + set y = 2 + i() + fun i() = + set z = 3 + g +f(0, 0, 0)() +//│ ═══[RUNTIME ERROR] Expected: 6, got: 1 +//│ = 1 + +:sjs +fun f(x, cond) = + set x = 1 + set x = 2 + if cond then + () => x + else + set x = 3 + () => x +//│ JS (unsanitized): +//│ let f28, lambda1, lambda2, lambda$1, lambda$2; +//│ lambda$2 = function lambda$(x1) { +//│ return x1; +//│ }; +//│ lambda1 = function lambda(x1) { +//│ return () => { +//│ return lambda$2(x1); +//│ }; +//│ }; +//│ lambda$1 = function lambda$(x1) { +//│ return x1; +//│ }; +//│ lambda2 = function lambda(x1) { +//│ return () => { +//│ return lambda$1(x1); +//│ }; +//│ }; +//│ f28 = function f(x1, cond) { +//│ x1 = 1; +//│ x1 = 2; +//│ if (cond === true) { +//│ return lambda1(x1) ?? null; +//│ } else { +//│ x1 = 3; +//│ return lambda2(x1) ?? null; +//│ } +//│ }; +//│ null From ef2bf3d4ff36435c6d9c90eb6890fadcae6b298f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 2 Feb 2025 01:20:32 +0800 Subject: [PATCH 040/127] Update tests --- .../src/test/mlscript/codegen/Classes.mls | 24 ++- .../src/test/mlscript/codegen/Lifter.mls | 104 ++++++----- .../mlscript/handlers/EffectsInClasses.mls | 4 +- .../mlscript/handlers/RecursiveHandlers.mls | 163 +++++++++--------- .../test/mlscript/handlers/StackSafety.mls | 92 +++++----- 5 files changed, 197 insertions(+), 190 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Classes.mls b/hkmc2/shared/src/test/mlscript/codegen/Classes.mls index d7c65f853..498760a86 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Classes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Classes.mls @@ -10,7 +10,9 @@ class Foo(arguments) //│ JS (unsanitized): //│ let Foo1; -//│ Foo1 = function Foo(arguments2) { return new Foo.class(arguments2); }; +//│ Foo1 = function Foo(arguments2) { +//│ return new Foo.class(arguments2); +//│ }; //│ Foo1.class = class Foo { //│ constructor(arguments1) { //│ this.arguments = arguments1; @@ -23,7 +25,9 @@ class Foo(arguments) class Foo(eval) //│ JS (unsanitized): //│ let Foo3; -//│ Foo3 = function Foo(eval2) { return new Foo.class(eval2); }; +//│ Foo3 = function Foo(eval2) { +//│ return new Foo.class(eval2); +//│ }; //│ Foo3.class = class Foo2 { //│ constructor(eval1) { //│ this.eval = eval1; @@ -36,7 +40,9 @@ class Foo(eval) class Foo(implements) //│ JS (unsanitized): //│ let Foo5; -//│ Foo5 = function Foo(implements2) { return new Foo.class(implements2); }; +//│ Foo5 = function Foo(implements2) { +//│ return new Foo.class(implements2); +//│ }; //│ Foo5.class = class Foo4 { //│ constructor(implements1) { //│ this.implements = implements1; @@ -49,7 +55,9 @@ class Foo(implements) class Foo(package) //│ JS (unsanitized): //│ let Foo7; -//│ Foo7 = function Foo(package2) { return new Foo.class(package2); }; +//│ Foo7 = function Foo(package2) { +//│ return new Foo.class(package2); +//│ }; //│ Foo7.class = class Foo6 { //│ constructor(package1) { //│ this.package = package1; @@ -62,7 +70,9 @@ class Foo(package) class Foo(protected) //│ JS (unsanitized): //│ let Foo9; -//│ Foo9 = function Foo(protected2) { return new Foo.class(protected2); }; +//│ Foo9 = function Foo(protected2) { +//│ return new Foo.class(protected2); +//│ }; //│ Foo9.class = class Foo8 { //│ constructor(protected1) { //│ this.protected = protected1; @@ -75,7 +85,9 @@ class Foo(protected) class Foo(static) //│ JS (unsanitized): //│ let Foo11; -//│ Foo11 = function Foo(static2) { return new Foo.class(static2); }; +//│ Foo11 = function Foo(static2) { +//│ return new Foo.class(static2); +//│ }; //│ Foo11.class = class Foo10 { //│ constructor(static1) { //│ this.static = static1; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls index e30cb2460..61e876894 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls @@ -45,7 +45,7 @@ fun f(used1, unused1) = f(1, 2) //│ JS (unsanitized): //│ let f1, g1, g$1; -//│ g$1 = function g$(used1, used2, g_arg) { +//│ g$1 = function g$(used2, used1, g_arg) { //│ return (g_arg2) => { //│ let used3, tmp, tmp1; //│ used3 = 2; @@ -54,16 +54,16 @@ f(1, 2) //│ return tmp1 + g_arg2; //│ }; //│ }; -//│ g1 = function g(used1, used2) { +//│ g1 = function g(used2, used1) { //│ return (g_arg) => { -//│ return g$1(used1, used2, g_arg); +//│ return g$1(used2, used1, g_arg); //│ }; //│ }; //│ f1 = function f(used1, unused1) { //│ let used2, unused2, tmp, tmp1; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g$1(used1, used2, used2); +//│ tmp = g$1(used2, used1, used2); //│ tmp1 = tmp(used2) ?? null; //│ return tmp1 + unused2; //│ }; @@ -151,7 +151,7 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let f5, g5, g$4; -//│ g$4 = function g$(a4, a3, a6, a2, a1, a5) { +//│ g$4 = function g$(a5, a1, a4, a3, a6, a2) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; @@ -159,20 +159,20 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6; //│ }; -//│ g5 = function g(a4, a3, a6, a2, a1, a5) { +//│ g5 = function g(a5, a1, a4, a3, a6, a2) { //│ return () => { -//│ return g$4(a4, a3, a6, a2, a1, a5); +//│ return g$4(a5, a1, a4, a3, a6, a2); //│ }; //│ }; //│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$4(a4, a3, a6, a2, a1, a5); +//│ tmp = g$4(a5, a1, a4, a3, a6, a2); //│ return tmp; //│ }; //│ f5(1, 2, 3, 4, 5, 6) //│ = 21 -:expect '01' +:expect "01" :sjs class Tuple(a, b) fun f() = @@ -245,12 +245,12 @@ x + y //│ tmp5 = tmp4.toString() ?? null; //│ y = tmp5; //│ x + y -//│ = '01' -//│ f1 = [Function (anonymous)] -//│ f2 = [Function (anonymous)] -//│ ret = Tuple { a: [Function (anonymous)], b: [Function (anonymous)] } -//│ x = '0' -//│ y = '1' +//│ = "01" +//│ f1 = [function] +//│ f2 = [function] +//│ ret = Tuple([function], [function]) +//│ x = "0" +//│ y = "1" class Tuple(a, b) fun f(used1) = @@ -263,7 +263,7 @@ fun f(used1) = fun g22() = used1 Tuple(g1(10), g22) -:expect '11110110' +:expect "11110110" let ret = f(1) let gRet = ret.a let g2 = ret.b @@ -275,19 +275,16 @@ hFun() let c = g2() let d = iFun() a.toString() + b + c + d -//│ = '11110110' +//│ = "11110110" //│ a = 11 //│ b = 1 //│ c = 10 //│ d = 110 -//│ g2 = [Function (anonymous)] -//│ gRet = Tuple2 { a: [Function (anonymous)], b: [Function (anonymous)] } -//│ hFun = [Function (anonymous)] -//│ iFun = [Function (anonymous)] -//│ > Tuple2 { -//│ > a: Tuple2 { a: [Function (anonymous)], b: [Function (anonymous)] }, -//│ > b: [Function (anonymous)] -//│ ret = } +//│ g2 = [function] +//│ gRet = Tuple([function], [function]) +//│ hFun = [function] +//│ iFun = [function] +//│ ret = Tuple(Tuple([function], [function]), [function]) // some variables mutated, some not :sjs @@ -547,6 +544,7 @@ fun f(used1, unused1) = new Test(unused1) f(1, 2).get() //│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor +//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' :expect 2 fun f(x) = @@ -635,7 +633,7 @@ class A(a) with fun g() = x g() A(2).f() -//│ ═══[RUNTIME ERROR] Expected: 2, got: null +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'null' :sjs fun g() = @@ -714,65 +712,65 @@ fun f() = f().foo() //│ JS (unsanitized): //│ let f25, Bad1, Good1, tmp30, Bad$ctor, Bad$, Good$ctor, Good$, f$capture19; -//│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { +//│ Good$ = function Good$(z$1, x$2, y$3, f$capture$0) { //│ let tmp31; //│ tmp31 = new Good1(); -//│ return tmp31(x$1, y$2, z$3, f$capture$0); +//│ return tmp31(z$1, x$2, y$3, f$capture$0); //│ }; -//│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { +//│ Good$ctor = function Good$ctor(z$1, x$2, y$3, f$capture$0) { //│ return () => { //│ let tmp31; //│ tmp31 = new Good1(); -//│ return tmp31(x$1, y$2, z$3, f$capture$0); +//│ return tmp31(z$1, x$2, y$3, f$capture$0); //│ }; //│ }; //│ Good1 = function Good() { -//│ return (x$11, y$21, z$31, f$capture$01) => { -//│ return new Good.class()(x$11, y$21, z$31, f$capture$01); +//│ return (z$11, x$21, y$31, f$capture$01) => { +//│ return new Good.class()(z$11, x$21, y$31, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (x$1, y$2, z$3, f$capture$0) => { -//│ this.x$1 = x$1; -//│ this.y$2 = y$2; -//│ this.z$3 = z$3; +//│ return (z$1, x$2, y$3, f$capture$0) => { +//│ this.z$1 = z$1; +//│ this.x$2 = x$2; +//│ this.y$3 = y$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } //│ } //│ foo() { //│ let tmp31, tmp32; -//│ this.z$3 = 100; -//│ tmp31 = this.x$1 + this.y$2; -//│ tmp32 = tmp31 + this.z$3; +//│ this.z$1 = 100; +//│ tmp31 = this.x$2 + this.y$3; +//│ tmp32 = tmp31 + this.z$1; //│ return tmp32 + this.f$capture$0.w0$; //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; -//│ Bad$ = function Bad$(x$1, y$2, z$3, f$capture$0) { +//│ Bad$ = function Bad$(z$1, x$2, y$3, f$capture$0) { //│ let tmp31; //│ tmp31 = new Bad1(); -//│ return tmp31(x$1, y$2, z$3, f$capture$0); +//│ return tmp31(z$1, x$2, y$3, f$capture$0); //│ }; -//│ Bad$ctor = function Bad$ctor(x$1, y$2, z$3, f$capture$0) { +//│ Bad$ctor = function Bad$ctor(z$1, x$2, y$3, f$capture$0) { //│ return () => { //│ let tmp31; //│ tmp31 = new Bad1(); -//│ return tmp31(x$1, y$2, z$3, f$capture$0); +//│ return tmp31(z$1, x$2, y$3, f$capture$0); //│ }; //│ }; //│ Bad1 = function Bad() { -//│ return (x$11, y$21, z$31, f$capture$01) => { -//│ return new Bad.class()(x$11, y$21, z$31, f$capture$01); +//│ return (z$11, x$21, y$31, f$capture$01) => { +//│ return new Bad.class()(z$11, x$21, y$31, f$capture$01); //│ } //│ }; //│ Bad1.class = class Bad { //│ constructor() { -//│ return (x$1, y$2, z$3, f$capture$0) => { -//│ this.x$1 = x$1; -//│ this.y$2 = y$2; -//│ this.z$3 = z$3; +//│ return (z$1, x$2, y$3, f$capture$0) => { +//│ this.z$1 = z$1; +//│ this.x$2 = x$2; +//│ this.y$3 = y$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } @@ -799,9 +797,9 @@ f().foo() //│ y1 = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp31 = Bad$(x1, y1, z, capture); +//│ tmp31 = Bad$(z, x1, y1, capture); //│ tmp32 = tmp31.foo() ?? null; -//│ return Good$(x1, y1, z, capture); +//│ return Good$(z, x1, y1, capture); //│ }; //│ tmp30 = f25(); //│ tmp30.foo() ?? null @@ -818,7 +816,7 @@ let ret = f() ret() ret() //│ = 2 -//│ ret = [Function (anonymous)] +//│ ret = [function] :expect 6 :fixme @@ -835,7 +833,7 @@ fun f(x, y, z) = set z = 3 g f(0, 0, 0)() -//│ ═══[RUNTIME ERROR] Expected: 6, got: 1 +//│ ═══[RUNTIME ERROR] Expected: '6', got: '1' //│ = 1 :sjs diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index e29cd5968..bc3a6c386 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -110,9 +110,9 @@ let oops = Lol(h) //│ > k //│ > b -//│ oops = Lol(Effect$h$) +//│ oops = Lol(Handler$h$()) oops.h -//│ = Effect$h$ +//│ = Handler$h$() diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index c2d3e97c3..a5ca4624a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -47,10 +47,7 @@ handle h2 = Effect with //│ ║ l.38: then h2.perform(arg - 1) + " " + arg //│ ╙── ^^ //│ > ––– -//│ > performing 2 -//│ > ––– -//│ > performing 3 -//│ = [undefined, undefined] +//│ ═══[RUNTIME ERROR] Error: Unhandled effects // The current implementation insert new handlers surrounding the entire handle block, and hence "later" handle block become the outer one @@ -121,41 +118,41 @@ if true do h1.perform(()) str //│ JS (unsanitized): -//│ let str, scrut, tmp8, tmp9, handleBlock$4; +//│ let str, scrut, tmp6, tmp7, handleBlock$4; //│ str = ""; //│ scrut = true; //│ if (scrut === true) { //│ handleBlock$4 = function handleBlock$() { -//│ let h1, tmp10, handleBlock$5, Cont$16, Handler$h1$2; +//│ let h1, tmp8, handleBlock$5, Cont$16, Handler$h1$2; //│ Handler$h1$2 = function Handler$h1$() { //│ return new Handler$h1$.class(); //│ }; //│ Handler$h1$2.class = class Handler$h1$1 extends Effect1 { //│ constructor() { -//│ let tmp11; -//│ tmp11 = super(); +//│ let tmp9; +//│ tmp9 = super(); //│ } //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(this, (k) => { -//│ let tmp11, tmp12, tmp13, res8, Cont$17; +//│ let tmp9, tmp10, tmp11, res5, Cont$17; //│ Cont$17 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$17.class = class Cont$12 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp14; -//│ tmp14 = super(null, null); +//│ let tmp12; +//│ tmp12 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 5) { -//│ res8 = value$; +//│ res5 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 5) { -//│ tmp12 = res8; -//│ tmp13 = str + "A"; -//│ str = tmp13; +//│ tmp10 = res5; +//│ tmp11 = str + "A"; +//│ str = tmp11; //│ return null; //│ } //│ break; @@ -163,17 +160,17 @@ str //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; +//│ tmp9 = str + "A"; +//│ str = tmp9; +//│ res5 = k(arg) ?? null; +//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { +//│ res5.tail.next = Cont$17(5); +//│ res5.tail = res5.tail.next; +//│ return res5; +//│ } +//│ tmp10 = res5; //│ tmp11 = str + "A"; //│ str = tmp11; -//│ res8 = k(arg) ?? null; -//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ res8.tail.next = Cont$17(5); -//│ res8.tail = res8.tail.next; -//│ return res8; -//│ } -//│ tmp12 = res8; -//│ tmp13 = str + "A"; -//│ str = tmp13; //│ return null; //│ }); //│ } @@ -185,23 +182,23 @@ str //│ }; //│ Cont$16.class = class Cont$13 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp11; -//│ tmp11 = super(null, null); +//│ let tmp9; +//│ tmp9 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 3) { -//│ tmp10 = value$; +//│ tmp8 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ if (tmp10 instanceof globalThis.Predef.__Return.class) { -//│ return tmp10; +//│ if (tmp8 instanceof globalThis.Predef.__Return.class) { +//│ return tmp8; //│ } //│ this.pc = 4; //│ continue contLoop; //│ } else if (this.pc === 4) { -//│ return tmp10; +//│ return tmp8; //│ } //│ break; //│ } @@ -209,37 +206,37 @@ str //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ handleBlock$5 = function handleBlock$() { -//│ let h2, tmp11, res8, res9, Cont$17, Handler$h2$2; +//│ let h2, tmp9, res5, res6, Cont$17, Handler$h2$2; //│ Handler$h2$2 = function Handler$h2$() { //│ return new Handler$h2$.class(); //│ }; //│ Handler$h2$2.class = class Handler$h2$1 extends Effect1 { //│ constructor() { -//│ let tmp12; -//│ tmp12 = super(); +//│ let tmp10; +//│ tmp10 = super(); //│ } //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(this, (k) => { -//│ let tmp12, tmp13, tmp14, tmp15, tmp16, res10, Cont$18; +//│ let tmp10, tmp11, tmp12, tmp13, tmp14, res7, Cont$18; //│ Cont$18 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$18.class = class Cont$14 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp17; -//│ tmp17 = super(null, null); +//│ let tmp15; +//│ tmp15 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 2) { -//│ res10 = value$; +//│ res7 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 2) { -//│ tmp14 = res10; -//│ tmp15 = str + "B"; -//│ tmp16 = str + tmp15; -//│ str = tmp16; +//│ tmp12 = res7; +//│ tmp13 = str + "B"; +//│ tmp14 = str + tmp13; +//│ str = tmp14; //│ return null; //│ } //│ break; @@ -247,19 +244,19 @@ str //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ tmp12 = str + "B"; -//│ tmp13 = str + tmp12; -//│ str = tmp13; -//│ res10 = k(arg) ?? null; -//│ if (res10 instanceof globalThis.Predef.__EffectSig.class) { -//│ res10.tail.next = Cont$18(2); -//│ res10.tail = res10.tail.next; -//│ return res10; +//│ tmp10 = str + "B"; +//│ tmp11 = str + tmp10; +//│ str = tmp11; +//│ res7 = k(arg) ?? null; +//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { +//│ res7.tail.next = Cont$18(2); +//│ res7.tail = res7.tail.next; +//│ return res7; //│ } -//│ tmp14 = res10; -//│ tmp15 = str + "B"; -//│ tmp16 = str + tmp15; -//│ str = tmp16; +//│ tmp12 = res7; +//│ tmp13 = str + "B"; +//│ tmp14 = str + tmp13; +//│ str = tmp14; //│ return null; //│ }); //│ } @@ -271,64 +268,64 @@ str //│ }; //│ Cont$17.class = class Cont$15 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp12; -//│ tmp12 = super(null, null); +//│ let tmp10; +//│ tmp10 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ res8 = value$; +//│ res5 = value$; //│ } else if (this.pc === 1) { -//│ res9 = value$; +//│ res6 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { -//│ tmp11 = res8; -//│ res9 = h1.perform(null) ?? null; -//│ if (res9 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp9 = res5; +//│ res6 = h1.perform(null) ?? null; +//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(res9, this); +//│ return globalThis.Predef.__appendInCont(res6, this); //│ } //│ this.pc = 1; //│ continue contLoop; //│ } else if (this.pc === 1) { -//│ return res9; +//│ return res6; //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ res8 = h2.perform(null) ?? null; -//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ res8.tail.next = Cont$17(0); -//│ return globalThis.Predef.__handleBlockImpl(res8, h2); +//│ res5 = h2.perform(null) ?? null; +//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { +//│ res5.tail.next = Cont$17(0); +//│ return globalThis.Predef.__handleBlockImpl(res5, h2); //│ } -//│ tmp11 = res8; -//│ res9 = h1.perform(null) ?? null; -//│ if (res9 instanceof globalThis.Predef.__EffectSig.class) { -//│ res9.tail.next = Cont$17(1); -//│ return globalThis.Predef.__handleBlockImpl(res9, h2); +//│ tmp9 = res5; +//│ res6 = h1.perform(null) ?? null; +//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { +//│ res6.tail.next = Cont$17(1); +//│ return globalThis.Predef.__handleBlockImpl(res6, h2); //│ } -//│ return res9; +//│ return res6; //│ }; -//│ tmp10 = handleBlock$5(); -//│ if (tmp10 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp10.tail.next = Cont$16(3); -//│ return globalThis.Predef.__handleBlockImpl(tmp10, h1); +//│ tmp8 = handleBlock$5(); +//│ if (tmp8 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp8.tail.next = Cont$16(3); +//│ return globalThis.Predef.__handleBlockImpl(tmp8, h1); //│ } -//│ if (tmp10 instanceof globalThis.Predef.__Return.class) { -//│ return tmp10; +//│ if (tmp8 instanceof globalThis.Predef.__Return.class) { +//│ return tmp8; //│ } -//│ return tmp10; +//│ return tmp8; //│ }; -//│ tmp8 = handleBlock$4(); -//│ if (tmp8 instanceof this.Predef.__EffectSig.class) { +//│ tmp6 = handleBlock$4(); +//│ if (tmp6 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ tmp9 = tmp8; +//│ tmp7 = tmp6; //│ } else { -//│ tmp9 = null; +//│ tmp7 = null; //│ } //│ str //│ = "BABABA" diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 4b42be534..26d16d89d 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -22,33 +22,33 @@ fun hi(n) = hi(0) //│ JS (unsanitized): //│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, hi$capture1, handleBlock$$capture1, lambda$capture1; -//│ Cont$$ = function Cont$$(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0, pc) { +//│ Cont$$ = function Cont$$(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0, pc) { //│ let tmp; //│ tmp = new Cont$3(pc); -//│ return tmp(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0); +//│ return tmp(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0); //│ }; -//│ Cont$$ctor = function Cont$$ctor(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0) { +//│ Cont$$ctor = function Cont$$ctor(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$3(pc); -//│ return tmp(scrut$1, tmp$2, dummy$3, n$4, hi$capture$0); +//│ return tmp(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0); //│ }; //│ }; //│ Cont$3 = function Cont$(pc1) { -//│ return (scrut$11, tmp$21, dummy$31, n$41, hi$capture$01) => { -//│ return new Cont$.class(pc1)(scrut$11, tmp$21, dummy$31, n$41, hi$capture$01); +//│ return (n$11, dummy$21, tmp$31, scrut$41, hi$capture$01) => { +//│ return new Cont$.class(pc1)(n$11, dummy$21, tmp$31, scrut$41, hi$capture$01); //│ } //│ }; //│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (scrut$1, tmp$2, dummy$3, n$4, hi$capture$0) => { +//│ return (n$1, dummy$2, tmp$3, scrut$4, hi$capture$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.scrut$1 = scrut$1; -//│ this.tmp$2 = tmp$2; -//│ this.dummy$3 = dummy$3; -//│ this.n$4 = n$4; +//│ this.n$1 = n$1; +//│ this.dummy$2 = dummy$2; +//│ this.tmp$3 = tmp$3; +//│ this.scrut$4 = scrut$4; //│ this.hi$capture$0 = hi$capture$0; //│ return this; //│ } @@ -59,20 +59,20 @@ hi(0) //│ } //│ contLoop: while (true) { //│ if (this.pc === 2) { -//│ this.scrut$1 = this.n$4 == 0; -//│ if (this.scrut$1 === true) { +//│ this.scrut$4 = this.n$1 == 0; +//│ if (this.scrut$4 === true) { //│ return 0; //│ } else { -//│ this.tmp$2 = this.n$4 - 1; +//│ this.tmp$3 = this.n$1 - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi(this.tmp$2); +//│ return hi(this.tmp$3); //│ } //│ this.pc = 1; //│ continue contLoop; //│ } else if (this.pc === 1) { //│ break contLoop; //│ } else if (this.pc === 0) { -//│ this.dummy$3 = this.hi$capture$0.res0$; +//│ this.dummy$2 = this.hi$capture$0.res0$; //│ this.pc = 2; //│ continue contLoop; //│ } @@ -100,7 +100,7 @@ hi(0) //│ if (scrut1 === true) { //│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); //│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$(scrut, tmp, dummy, n, capture, 0); +//│ capture.res0$.tail.next = Cont$$(n, dummy, tmp, scrut, capture, 0); //│ capture.res0$.tail = capture.res0$.tail.next; //│ return capture.res0$; //│ } @@ -303,37 +303,37 @@ fun sum(n) = sum(10000) //│ JS (unsanitized): //│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, sum$capture1, handleBlock$$capture3, lambda$capture3; -//│ Cont$$3 = function Cont$$(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0, pc) { +//│ Cont$$3 = function Cont$$(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0, pc) { //│ let tmp; //│ tmp = new Cont$9(pc); -//│ return tmp(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0); +//│ return tmp(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0); //│ }; -//│ Cont$$ctor3 = function Cont$$ctor(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0) { +//│ Cont$$ctor3 = function Cont$$ctor(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$9(pc); -//│ return tmp(offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0); +//│ return tmp(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0); //│ }; //│ }; //│ Cont$9 = function Cont$(pc1) { -//│ return (offsetGtDepth$11, n$21, tmp$31, prevDepth$41, tmp$51, scrut$61, dummy$71, tmp$81, sum$capture$01) => { -//│ return new Cont$.class(pc1)(offsetGtDepth$11, n$21, tmp$31, prevDepth$41, tmp$51, scrut$61, dummy$71, tmp$81, sum$capture$01); +//│ return (n$11, scrut$21, tmp$31, tmp$41, offsetGtDepth$51, tmp$61, prevDepth$71, dummy$81, sum$capture$01) => { +//│ return new Cont$.class(pc1)(n$11, scrut$21, tmp$31, tmp$41, offsetGtDepth$51, tmp$61, prevDepth$71, dummy$81, sum$capture$01); //│ } //│ }; //│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (offsetGtDepth$1, n$2, tmp$3, prevDepth$4, tmp$5, scrut$6, dummy$7, tmp$8, sum$capture$0) => { +//│ return (n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.offsetGtDepth$1 = offsetGtDepth$1; -//│ this.n$2 = n$2; +//│ this.n$1 = n$1; +//│ this.scrut$2 = scrut$2; //│ this.tmp$3 = tmp$3; -//│ this.prevDepth$4 = prevDepth$4; -//│ this.tmp$5 = tmp$5; -//│ this.scrut$6 = scrut$6; -//│ this.dummy$7 = dummy$7; -//│ this.tmp$8 = tmp$8; +//│ this.tmp$4 = tmp$4; +//│ this.offsetGtDepth$5 = offsetGtDepth$5; +//│ this.tmp$6 = tmp$6; +//│ this.prevDepth$7 = prevDepth$7; +//│ this.dummy$8 = dummy$8; //│ this.sum$capture$0 = sum$capture$0; //│ return this; //│ } @@ -346,14 +346,14 @@ sum(10000) //│ } //│ contLoop: while (true) { //│ if (this.pc === 4) { -//│ this.scrut$6 = this.n$2 == 0; -//│ if (this.scrut$6 === true) { +//│ this.scrut$2 = this.n$1 == 0; +//│ if (this.scrut$2 === true) { //│ return 0; //│ } else { -//│ this.tmp$3 = this.n$2 - 1; -//│ this.prevDepth$4 = globalThis.Predef.__stackDepth; +//│ this.tmp$6 = this.n$1 - 1; +//│ this.prevDepth$7 = globalThis.Predef.__stackDepth; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ this.sum$capture$0.res1$ = sum1(this.tmp$3); +//│ this.sum$capture$0.res1$ = sum1(this.tmp$6); //│ if (this.sum$capture$0.res1$ instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; //│ return globalThis.Predef.__appendInCont(this.sum$capture$0.res1$, this); @@ -366,21 +366,21 @@ sum(10000) //│ } else if (this.pc === 2) { //│ break contLoop; //│ } else if (this.pc === 1) { -//│ this.tmp$8 = this.sum$capture$0.res1$; -//│ globalThis.Predef.__stackDepth = this.prevDepth$4; -//│ this.offsetGtDepth$1 = this.prevDepth$4 < globalThis.Predef.__stackOffset; -//│ if (this.offsetGtDepth$1 === true) { -//│ globalThis.Predef.__stackOffset = this.prevDepth$4; +//│ this.tmp$4 = this.sum$capture$0.res1$; +//│ globalThis.Predef.__stackDepth = this.prevDepth$7; +//│ this.offsetGtDepth$5 = this.prevDepth$7 < globalThis.Predef.__stackOffset; +//│ if (this.offsetGtDepth$5 === true) { +//│ globalThis.Predef.__stackOffset = this.prevDepth$7; //│ this.pc = 3; //│ continue contLoop; //│ } //│ this.pc = 3; //│ continue contLoop; //│ } else if (this.pc === 3) { -//│ this.tmp$5 = this.tmp$8; -//│ return this.n$2 + this.tmp$5; +//│ this.tmp$3 = this.tmp$4; +//│ return this.n$1 + this.tmp$3; //│ } else if (this.pc === 0) { -//│ this.dummy$7 = this.sum$capture$0.res0$; +//│ this.dummy$8 = this.sum$capture$0.res0$; //│ this.pc = 4; //│ continue contLoop; //│ } @@ -409,7 +409,7 @@ sum(10000) //│ if (scrut1 === true) { //│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); //│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$3(offsetGtDepth, n, tmp, prevDepth, tmp1, scrut, dummy, tmp2, capture, 0); +//│ capture.res0$.tail.next = Cont$$3(n, scrut, tmp1, tmp2, offsetGtDepth, tmp, prevDepth, dummy, capture, 0); //│ capture.res0$.tail = capture.res0$.tail.next; //│ return capture.res0$; //│ } @@ -424,7 +424,7 @@ sum(10000) //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; //│ capture.res1$ = sum1(tmp); //│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res1$.tail.next = Cont$$3(offsetGtDepth, n, tmp, prevDepth, tmp1, scrut, dummy, tmp2, capture, 1); +//│ capture.res1$.tail.next = Cont$$3(n, scrut, tmp1, tmp2, offsetGtDepth, tmp, prevDepth, dummy, capture, 1); //│ capture.res1$.tail = capture.res1$.tail.next; //│ return capture.res1$; //│ } From 76953666a498f76e90ba17a894107c232aa02296 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 2 Feb 2025 01:25:18 +0800 Subject: [PATCH 041/127] Update tests --- .../test/mlscript/handlers/StackSafety.mls | 704 ++++++------------ 1 file changed, 232 insertions(+), 472 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 26d16d89d..12f3f5ca9 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -1,5 +1,4 @@ :js -:lift // sanity check :expect 5050 @@ -21,90 +20,58 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, hi$capture1, handleBlock$$capture1, lambda$capture1; -//│ Cont$$ = function Cont$$(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0, pc) { -//│ let tmp; -//│ tmp = new Cont$3(pc); -//│ return tmp(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0); -//│ }; -//│ Cont$$ctor = function Cont$$ctor(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$3(pc); -//│ return tmp(n$1, dummy$2, tmp$3, scrut$4, hi$capture$0); +//│ let hi, res, handleBlock$; +//│ hi = function hi(n) { +//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, res1, Cont$3; +//│ Cont$3 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ }; -//│ Cont$3 = function Cont$(pc1) { -//│ return (n$11, dummy$21, tmp$31, scrut$41, hi$capture$01) => { -//│ return new Cont$.class(pc1)(n$11, dummy$21, tmp$31, scrut$41, hi$capture$01); -//│ } -//│ }; -//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (n$1, dummy$2, tmp$3, scrut$4, hi$capture$0) => { -//│ let tmp; -//│ tmp = super(null, null); +//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp1; +//│ tmp1 = super(null, null); //│ this.pc = pc; -//│ this.n$1 = n$1; -//│ this.dummy$2 = dummy$2; -//│ this.tmp$3 = tmp$3; -//│ this.scrut$4 = scrut$4; -//│ this.hi$capture$0 = hi$capture$0; -//│ return this; //│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ this.hi$capture$0.res0$ = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 2) { -//│ this.scrut$4 = this.n$1 == 0; -//│ if (this.scrut$4 === true) { -//│ return 0; -//│ } else { -//│ this.tmp$3 = this.n$1 - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi(this.tmp$3); +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ res1 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 2) { +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0; +//│ } else { +//│ tmp = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ return hi(tmp); +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ break contLoop; +//│ } else if (this.pc === 0) { +//│ dummy = res1; +//│ this.pc = 2; +//│ continue contLoop; //│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ break contLoop; -//│ } else if (this.pc === 0) { -//│ this.dummy$2 = this.hi$capture$0.res0$; -//│ this.pc = 2; -//│ continue contLoop; +//│ break; //│ } -//│ break; //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ hi$capture1 = function hi$capture(res0$1) { -//│ return new hi$capture.class(res0$1); -//│ }; -//│ hi$capture1.class = class hi$capture { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "hi$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ hi = function hi(n) { -//│ let scrut, tmp, diff, diffGeqLimit, handlerExists, scrut1, dummy, capture; -//│ capture = new hi$capture1(null); +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; //│ handlerExists = globalThis.Predef.__stackHandler !== undefined; //│ scrut1 = diffGeqLimit && handlerExists; //│ if (scrut1 === true) { -//│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$(n, dummy, tmp, scrut, capture, 0); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$; +//│ res1 = globalThis.Predef.__stackHandler.perform(); +//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { +//│ res1.tail.next = Cont$3(0); +//│ res1.tail = res1.tail.next; +//│ return res1; //│ } -//│ dummy = capture.res0$; +//│ dummy = res1; //│ } //│ scrut = n == 0; //│ if (scrut === true) { @@ -115,173 +82,88 @@ hi(0) //│ return hi(tmp); //│ } //│ }; -//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { -//│ let tmp; -//│ tmp = new StackDelay$1(); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { -//│ return () => { -//│ let tmp; -//│ tmp = new StackDelay$1(); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ }; -//│ Cont$$2 = function Cont$$(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1, pc) { -//│ let tmp; -//│ tmp = new Cont$5(pc); -//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); -//│ }; -//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$5(pc); -//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ handleBlock$ = function handleBlock$() { +//│ let stackHandler, res1, Cont$3, StackDelay$1; +//│ StackDelay$1 = function StackDelay$() { +//│ return new StackDelay$.class(); //│ }; -//│ }; -//│ Cont$5 = function Cont$(pc1) { -//│ return (StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11); -//│ } -//│ }; -//│ Cont$5.class = class Cont$1 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) => { +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { +//│ constructor() { //│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ this.StackDelay$$instance$3 = StackDelay$$instance$3; -//│ this.res$2 = res$2; -//│ this.lambda$capture$0 = lambda$capture$0; -//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 4) { -//│ this.lambda$capture$0.res0$ = value$; +//│ tmp = super(); //│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ this.res$2 = this.lambda$capture$0.res0$; -//│ return this.res$2; -//│ } -//│ break; +//│ perform() { +//│ return globalThis.Predef.__mkEffect(this, (resume) => { +//│ let res2, res3, Cont$4; +//│ Cont$4 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 4) { +//│ res3 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ res2 = res3; +//│ return res2; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ res3 = resume(); +//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { +//│ res3.tail.next = Cont$4(4); +//│ res3.tail = res3.tail.next; +//│ return res3; +//│ } +//│ res2 = res3; +//│ return res2; +//│ }); //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ lambda$capture1 = function lambda$capture(res0$1) { -//│ return new lambda$capture.class(res0$1); -//│ }; -//│ lambda$capture1.class = class lambda$capture { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ lambda$ = function lambda$(StackDelay$$instance, handleBlock$$capture2, resume) { -//│ let res1, capture; -//│ capture = new lambda$capture1(null); -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ capture.res0$ = resume(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$2(StackDelay$$instance, res1, capture, handleBlock$$capture2, 4); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$; -//│ } -//│ res1 = capture.res0$; -//│ return res1; -//│ }; -//│ lambda = function lambda(StackDelay$$instance, handleBlock$$capture2) { -//│ return (resume) => { -//│ return lambda$(StackDelay$$instance, handleBlock$$capture2, resume); +//│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ }; -//│ StackDelay$1 = function StackDelay$() { -//│ return (handleBlock$$capture$01) => { -//│ return new StackDelay$.class()(handleBlock$$capture$01); -//│ } -//│ }; -//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay.class { -//│ constructor() { -//│ return (handleBlock$$capture$0) => { -//│ let tmp; -//│ tmp = super(); -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = lambda(this, this.handleBlock$$capture$0) ?? null; -//│ return globalThis.Predef.__mkEffect(this, lambda$this); -//│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ Cont$$1 = function Cont$$(handleBlock$$capture$0, pc) { -//│ let tmp; -//│ tmp = new Cont$4(pc); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ Cont$$ctor1 = function Cont$$ctor(handleBlock$$capture$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$4(pc); -//│ return tmp(handleBlock$$capture$0); +//│ stackHandler = StackDelay$1(); +//│ Cont$3 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ }; -//│ Cont$4 = function Cont$(pc1) { -//│ return (handleBlock$$capture$01) => { -//│ return new Cont$.class(pc1)(handleBlock$$capture$01); -//│ } -//│ }; -//│ Cont$4.class = class Cont$2 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (handleBlock$$capture$0) => { +//│ Cont$3.class = class Cont$2 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; //│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 3) { -//│ this.handleBlock$$capture$0.res0$ = value$; -//│ } -//│ contLoop: while (true) { +//│ resume(value$) { //│ if (this.pc === 3) { -//│ return this.handleBlock$$capture$0.res0$; +//│ res1 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 3) { +//│ return res1; +//│ } +//│ break; //│ } -//│ break; //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$$capture1 = function handleBlock$$capture(res0$1) { -//│ return new handleBlock$$capture.class(res0$1); -//│ }; -//│ handleBlock$$capture1.class = class handleBlock$$capture { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ handleBlock$ = function handleBlock$() { -//│ let stackHandler, capture; -//│ capture = new handleBlock$$capture1(null); -//│ stackHandler = StackDelay$$(capture); +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; //│ globalThis.Predef.__stackLimit = 5; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ capture.res0$ = hi(0); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$1(capture, 3); -//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, stackHandler); +//│ res1 = hi(0); +//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { +//│ res1.tail.next = Cont$3(3); +//│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler); //│ } -//│ return capture.res0$; +//│ return res1; //│ }; //│ res = handleBlock$(); //│ if (res instanceof this.Predef.__EffectSig.class) { @@ -302,118 +184,81 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, sum$capture1, handleBlock$$capture3, lambda$capture3; -//│ Cont$$3 = function Cont$$(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0, pc) { -//│ let tmp; -//│ tmp = new Cont$9(pc); -//│ return tmp(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0); -//│ }; -//│ Cont$$ctor3 = function Cont$$ctor(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$9(pc); -//│ return tmp(n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0); +//│ let sum1, res1, handleBlock$1; +//│ sum1 = function sum(n) { +//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, res2, res3, Cont$6; +//│ Cont$6 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ }; -//│ Cont$9 = function Cont$(pc1) { -//│ return (n$11, scrut$21, tmp$31, tmp$41, offsetGtDepth$51, tmp$61, prevDepth$71, dummy$81, sum$capture$01) => { -//│ return new Cont$.class(pc1)(n$11, scrut$21, tmp$31, tmp$41, offsetGtDepth$51, tmp$61, prevDepth$71, dummy$81, sum$capture$01); -//│ } -//│ }; -//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (n$1, scrut$2, tmp$3, tmp$4, offsetGtDepth$5, tmp$6, prevDepth$7, dummy$8, sum$capture$0) => { -//│ let tmp; -//│ tmp = super(null, null); +//│ Cont$6.class = class Cont$3 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp3; +//│ tmp3 = super(null, null); //│ this.pc = pc; -//│ this.n$1 = n$1; -//│ this.scrut$2 = scrut$2; -//│ this.tmp$3 = tmp$3; -//│ this.tmp$4 = tmp$4; -//│ this.offsetGtDepth$5 = offsetGtDepth$5; -//│ this.tmp$6 = tmp$6; -//│ this.prevDepth$7 = prevDepth$7; -//│ this.dummy$8 = dummy$8; -//│ this.sum$capture$0 = sum$capture$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 1) { -//│ this.sum$capture$0.res1$ = value$; -//│ } else if (this.pc === 0) { -//│ this.sum$capture$0.res0$ = value$; //│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ this.scrut$2 = this.n$1 == 0; -//│ if (this.scrut$2 === true) { -//│ return 0; -//│ } else { -//│ this.tmp$6 = this.n$1 - 1; -//│ this.prevDepth$7 = globalThis.Predef.__stackDepth; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ this.sum$capture$0.res1$ = sum1(this.tmp$6); -//│ if (this.sum$capture$0.res1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ resume(value$) { +//│ if (this.pc === 1) { +//│ res3 = value$; +//│ } else if (this.pc === 0) { +//│ res2 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0; +//│ } else { +//│ tmp = n - 1; +//│ prevDepth = globalThis.Predef.__stackDepth; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ res3 = sum1(tmp); +//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 1; +//│ return globalThis.Predef.__appendInCont(res3, this); +//│ } //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.res1$, this); +//│ continue contLoop; //│ } -//│ this.pc = 1; +//│ this.pc = 2; //│ continue contLoop; -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ break contLoop; -//│ } else if (this.pc === 1) { -//│ this.tmp$4 = this.sum$capture$0.res1$; -//│ globalThis.Predef.__stackDepth = this.prevDepth$7; -//│ this.offsetGtDepth$5 = this.prevDepth$7 < globalThis.Predef.__stackOffset; -//│ if (this.offsetGtDepth$5 === true) { -//│ globalThis.Predef.__stackOffset = this.prevDepth$7; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 1) { +//│ tmp2 = res3; +//│ globalThis.Predef.__stackDepth = prevDepth; +//│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; +//│ if (offsetGtDepth === true) { +//│ globalThis.Predef.__stackOffset = prevDepth; +//│ this.pc = 3; +//│ continue contLoop; +//│ } //│ this.pc = 3; //│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ tmp1 = tmp2; +//│ return n + tmp1; +//│ } else if (this.pc === 0) { +//│ dummy = res2; +//│ this.pc = 4; +//│ continue contLoop; //│ } -//│ this.pc = 3; -//│ continue contLoop; -//│ } else if (this.pc === 3) { -//│ this.tmp$3 = this.tmp$4; -//│ return this.n$1 + this.tmp$3; -//│ } else if (this.pc === 0) { -//│ this.dummy$8 = this.sum$capture$0.res0$; -//│ this.pc = 4; -//│ continue contLoop; +//│ break; //│ } -//│ break; //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ sum$capture1 = function sum$capture(res0$1, res1$1) { -//│ return new sum$capture.class(res0$1, res1$1); -//│ }; -//│ sum$capture1.class = class sum$capture { -//│ constructor(res0$, res1$) { -//│ this.res0$ = res0$; -//│ this.res1$ = res1$; -//│ } -//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.res0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } -//│ }; -//│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, tmp2, offsetGtDepth, prevDepth, diff, diffGeqLimit, handlerExists, scrut1, dummy, capture; -//│ capture = new sum$capture1(null, null); +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; //│ diff = globalThis.Predef.__stackDepth - globalThis.Predef.__stackOffset; //│ diffGeqLimit = diff >= globalThis.Predef.__stackLimit; //│ handlerExists = globalThis.Predef.__stackHandler !== undefined; //│ scrut1 = diffGeqLimit && handlerExists; //│ if (scrut1 === true) { -//│ capture.res0$ = globalThis.Predef.__stackHandler.perform(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$3(n, scrut, tmp1, tmp2, offsetGtDepth, tmp, prevDepth, dummy, capture, 0); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$; +//│ res2 = globalThis.Predef.__stackHandler.perform(); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$6(0); +//│ res2.tail = res2.tail.next; +//│ return res2; //│ } -//│ dummy = capture.res0$; +//│ dummy = res2; //│ } //│ scrut = n == 0; //│ if (scrut === true) { @@ -422,13 +267,13 @@ sum(10000) //│ tmp = n - 1; //│ prevDepth = globalThis.Predef.__stackDepth; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ capture.res1$ = sum1(tmp); -//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res1$.tail.next = Cont$$3(n, scrut, tmp1, tmp2, offsetGtDepth, tmp, prevDepth, dummy, capture, 1); -//│ capture.res1$.tail = capture.res1$.tail.next; -//│ return capture.res1$; +//│ res3 = sum1(tmp); +//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { +//│ res3.tail.next = Cont$6(1); +//│ res3.tail = res3.tail.next; +//│ return res3; //│ } -//│ tmp2 = capture.res1$; +//│ tmp2 = res3; //│ globalThis.Predef.__stackDepth = prevDepth; //│ offsetGtDepth = prevDepth < globalThis.Predef.__stackOffset; //│ if (offsetGtDepth === true) { @@ -438,173 +283,88 @@ sum(10000) //│ return n + tmp1; //│ } //│ }; -//│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { -//│ let tmp; -//│ tmp = new StackDelay$3(); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ StackDelay$$ctor1 = function StackDelay$$ctor(handleBlock$$capture$0) { -//│ return () => { -//│ let tmp; -//│ tmp = new StackDelay$3(); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ }; -//│ Cont$$5 = function Cont$$(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1, pc) { -//│ let tmp; -//│ tmp = new Cont$11(pc); -//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); -//│ }; -//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$11(pc); -//│ return tmp(StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1); +//│ handleBlock$1 = function handleBlock$() { +//│ let stackHandler, res2, Cont$6, StackDelay$2; +//│ StackDelay$2 = function StackDelay$() { +//│ return new StackDelay$.class(); //│ }; -//│ }; -//│ Cont$11 = function Cont$(pc1) { -//│ return (StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$31, res$21, lambda$capture$01, handleBlock$$capture$11); -//│ } -//│ }; -//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (StackDelay$$instance$3, res$2, lambda$capture$0, handleBlock$$capture$1) => { +//│ StackDelay$2.class = class StackDelay$1 extends globalThis.Predef.__StackDelay.class { +//│ constructor() { //│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ this.StackDelay$$instance$3 = StackDelay$$instance$3; -//│ this.res$2 = res$2; -//│ this.lambda$capture$0 = lambda$capture$0; -//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 6) { -//│ this.lambda$capture$0.res0$ = value$; +//│ tmp = super(); //│ } -//│ contLoop: while (true) { -//│ if (this.pc === 6) { -//│ this.res$2 = this.lambda$capture$0.res0$; -//│ return this.res$2; -//│ } -//│ break; +//│ perform() { +//│ return globalThis.Predef.__mkEffect(this, (resume) => { +//│ let res3, res4, Cont$7; +//│ Cont$7 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$7.class = class Cont$4 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 6) { +//│ res4 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 6) { +//│ res3 = res4; +//│ return res3; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ res4 = resume(); +//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { +//│ res4.tail.next = Cont$7(6); +//│ res4.tail = res4.tail.next; +//│ return res4; +//│ } +//│ res3 = res4; +//│ return res3; +//│ }); //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ lambda$capture3 = function lambda$capture(res0$1) { -//│ return new lambda$capture.class(res0$1); -//│ }; -//│ lambda$capture3.class = class lambda$capture2 { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ lambda$1 = function lambda$(StackDelay$$instance, handleBlock$$capture4, resume) { -//│ let res2, capture; -//│ capture = new lambda$capture3(null); -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ capture.res0$ = resume(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, res2, capture, handleBlock$$capture4, 6); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$; -//│ } -//│ res2 = capture.res0$; -//│ return res2; -//│ }; -//│ lambda1 = function lambda(StackDelay$$instance, handleBlock$$capture4) { -//│ return (resume) => { -//│ return lambda$1(StackDelay$$instance, handleBlock$$capture4, resume); +//│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ }; -//│ StackDelay$3 = function StackDelay$() { -//│ return (handleBlock$$capture$01) => { -//│ return new StackDelay$.class()(handleBlock$$capture$01); -//│ } -//│ }; -//│ StackDelay$3.class = class StackDelay$2 extends globalThis.Predef.__StackDelay.class { -//│ constructor() { -//│ return (handleBlock$$capture$0) => { -//│ let tmp; -//│ tmp = super(); -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = lambda1(this, this.handleBlock$$capture$0) ?? null; -//│ return globalThis.Predef.__mkEffect(this, lambda$this); -//│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { -//│ let tmp; -//│ tmp = new Cont$10(pc); -//│ return tmp(handleBlock$$capture$0); -//│ }; -//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$10(pc); -//│ return tmp(handleBlock$$capture$0); +//│ stackHandler = StackDelay$2(); +//│ Cont$6 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ }; -//│ Cont$10 = function Cont$(pc1) { -//│ return (handleBlock$$capture$01) => { -//│ return new Cont$.class(pc1)(handleBlock$$capture$01); -//│ } -//│ }; -//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (handleBlock$$capture$0) => { +//│ Cont$6.class = class Cont$5 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 5) { -//│ this.handleBlock$$capture$0.res0$ = value$; //│ } -//│ contLoop: while (true) { +//│ resume(value$) { //│ if (this.pc === 5) { -//│ return this.handleBlock$$capture$0.res0$; +//│ res2 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 5) { +//│ return res2; +//│ } +//│ break; //│ } -//│ break; //│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$$capture3 = function handleBlock$$capture(res0$1) { -//│ return new handleBlock$$capture.class(res0$1); -//│ }; -//│ handleBlock$$capture3.class = class handleBlock$$capture2 { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ handleBlock$1 = function handleBlock$() { -//│ let stackHandler, capture; -//│ capture = new handleBlock$$capture3(null); -//│ stackHandler = StackDelay$$1(capture); +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; //│ globalThis.Predef.__stackLimit = 1000; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = stackHandler; -//│ capture.res0$ = sum1(10000); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$4(capture, 5); -//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, stackHandler); +//│ res2 = sum1(10000); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$6(5); +//│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler); //│ } -//│ return capture.res0$; +//│ return res2; //│ }; //│ res1 = handleBlock$1(); //│ if (res1 instanceof this.Predef.__EffectSig.class) { From 3c353d27da58abe434215a7d377f3e8edba07556 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 2 Feb 2025 23:33:18 +0800 Subject: [PATCH 042/127] add scc partioning algo --- core/shared/main/scala/utils/algorithms.scala | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/core/shared/main/scala/utils/algorithms.scala b/core/shared/main/scala/utils/algorithms.scala index 0fa96cdef..d961325b7 100644 --- a/core/shared/main/scala/utils/algorithms.scala +++ b/core/shared/main/scala/utils/algorithms.scala @@ -2,6 +2,8 @@ package mlscript.utils import scala.annotation.tailrec import scala.collection.immutable.SortedMap +import scala.collection.mutable.ArrayBuffer + object algorithms { final class CyclicGraphError(message: String) extends Exception(message) @@ -29,4 +31,75 @@ object algorithms { } sort(toPred, Seq()) } + + private case class SccNode[A]( + val node: A, + val num: Int, + var lowlink: Int = -1, + var visited: Boolean = false, + var processed: Boolean = false + ) + + /** + * Partitions a graph into its strongly connected components. The input type must be able to + * be hashed efficiently as it will be used as a key. + * + * @param edges The edges of the graph. + * @param nodes Any additional nodes that are not necessarily in the edges list. + * @return A list of strongly connected components of the graph. + */ + def partitionScc[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { + // pre-process: assign each node an id + val edgesSet = edges.toSet + val nodesUniq = (edgesSet.flatMap { case (a, b) => Set(a, b) } ++ nodes.toSet).toList + val nodesN = nodesUniq.zipWithIndex.map { case (node, idx) => SccNode(node, idx) } + val nodeToIdx = nodesN.map(node => node.node -> node.num).toMap + val nodesIdx = nodeToIdx.map { case (node, idx) => idx -> SccNode(node, idx) } + + val neighbours = edges + .map { case (a, b) => (nodeToIdx(a), nodesIdx(nodeToIdx(b))) } + .groupBy(_._1) + .map { case (a, b) => a -> b.map(_._2) } + + var stack: List[SccNode[A]] = List.empty + var sccs: List[List[A]] = List.empty + var i = 0 + + def dfs(node: SccNode[A]): Unit = { + node.lowlink = node.num + node.visited = true + stack = node :: stack + i += 1 + for (n <- neighbours(node.num)) { + if (!n.visited) { + dfs(n) + node.lowlink = n.lowlink.min(node.lowlink) + } else if (!n.processed) { + node.lowlink = n.num.min(node.lowlink) + } + } + node.processed = true + if (node.lowlink == node.num) { + var scc: List[A] = List.empty + var cur = stack.head + stack = stack.tail + while (cur.num != node.num) { + scc = node.node :: scc + cur = stack.head + stack = stack.tail + } + sccs = scc :: sccs + } + } + + for (n <- nodesN) { + if (!n.visited) dfs(n) + } + sccs + } + + // TODO + def sscsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { + ??? + } } From 3aceba777ab7c7041ad6bf3fc45e6f6bac944bf2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 17:06:02 +0800 Subject: [PATCH 043/127] re-organize tests --- .../src/test/mlscript/lifter/ClassInFun.mls | 247 +++++++++++ .../Lifter.mls => lifter/FunInFun.mls} | 407 +++--------------- .../src/test/mlscript/lifter/FunInMethod.mls | 12 + 3 files changed, 328 insertions(+), 338 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls rename hkmc2/shared/src/test/mlscript/{codegen/Lifter.mls => lifter/FunInFun.mls} (60%) create mode 100644 hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls new file mode 100644 index 000000000..aad5be577 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -0,0 +1,247 @@ +:lift +:js + +// make sure class fields are discriminated properly for immutable captures when supported +:handler +abstract class Effect with + fun perform() +handle h = Effect with + fun perform()(k) = k() +fun f() = + h.perform() + 1 +f() + f() + f() +//│ = 3 + +:expect 1 +:sjs +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + Test(unused1) +f(1, 2).get() +//│ JS (unsanitized): +//│ let f1, Test1, g, h, tmp1, Test$ctor, Test$, g$, h$; +//│ h$ = function h$(used3, used1) { +//│ return used3; +//│ }; +//│ h = function h(used3, used1) { +//│ return () => { +//│ return h$(used3, used1); +//│ }; +//│ }; +//│ g$ = function g$(used1, g_arg) { +//│ let used3, tmp2; +//│ used3 = 2; +//│ tmp2 = h$(used3, used1); +//│ return used1 + tmp2; +//│ }; +//│ g = function g(used1) { +//│ return (g_arg) => { +//│ return g$(used1, g_arg); +//│ }; +//│ }; +//│ Test$ = function Test$(used1$0, a) { +//│ let tmp2; +//│ tmp2 = new Test1(a); +//│ return tmp2(used1$0); +//│ }; +//│ Test$ctor = function Test$ctor(used1$0) { +//│ return (a) => { +//│ let tmp2; +//│ tmp2 = new Test1(a); +//│ return tmp2(used1$0); +//│ }; +//│ }; +//│ Test1 = function Test(a1) { +//│ return (used1$01) => { +//│ return new Test.class(a1)(used1$01); +//│ } +//│ }; +//│ Test1.class = class Test { +//│ constructor(a) { +//│ return (used1$0) => { +//│ this.a = a; +//│ this.used1$0 = used1$0; +//│ return this; +//│ } +//│ } +//│ get() { +//│ return this.used1$0; +//│ } +//│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } +//│ }; +//│ f1 = function f(used1, unused1) { +//│ let unused2; +//│ unused2 = 2; +//│ return Test$(used1, unused1); +//│ }; +//│ tmp1 = f1(1, 2); +//│ tmp1.get() ?? null +//│ = 1 + +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + let foo = Test + foo(unused1) +f(1, 2).get() +//│ = 1 + +:fixme +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + new Test(unused1) +f(1, 2).get() +//│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor +//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' + +:expect 2 +fun f(x) = + class A() with + fun f() = set x = 2 + A().f() + x +f(1) +//│ = 2 + +// only w should be in a capture +:expect 10111 +:sjs +fun f() = + let x = 1 + let y = 10 + let z = 10 + let w = 1000 + class Good() with + fun foo() = + set z = 100 + x + y + z + w + class Bad() with + fun foo() = + set w = 10000 + Bad().foo() + Good() +f().foo() +//│ JS (unsanitized): +//│ let f5, Bad1, Good1, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; +//│ Good$ = function Good$(y$1, z$2, x$3, f$capture$0) { +//│ let tmp5; +//│ tmp5 = new Good1(); +//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ }; +//│ Good$ctor = function Good$ctor(y$1, z$2, x$3, f$capture$0) { +//│ return () => { +//│ let tmp5; +//│ tmp5 = new Good1(); +//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ }; +//│ }; +//│ Good1 = function Good() { +//│ return (y$11, z$21, x$31, f$capture$01) => { +//│ return new Good.class()(y$11, z$21, x$31, f$capture$01); +//│ } +//│ }; +//│ Good1.class = class Good { +//│ constructor() { +//│ return (y$1, z$2, x$3, f$capture$0) => { +//│ this.y$1 = y$1; +//│ this.z$2 = z$2; +//│ this.x$3 = x$3; +//│ this.f$capture$0 = f$capture$0; +//│ return this; +//│ } +//│ } +//│ foo() { +//│ let tmp5, tmp6; +//│ this.z$2 = 100; +//│ tmp5 = this.x$3 + this.y$1; +//│ tmp6 = tmp5 + this.z$2; +//│ return tmp6 + this.f$capture$0.w0$; +//│ } +//│ toString() { return "Good(" + "" + ")"; } +//│ }; +//│ Bad$ = function Bad$(y$1, z$2, x$3, f$capture$0) { +//│ let tmp5; +//│ tmp5 = new Bad1(); +//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ }; +//│ Bad$ctor = function Bad$ctor(y$1, z$2, x$3, f$capture$0) { +//│ return () => { +//│ let tmp5; +//│ tmp5 = new Bad1(); +//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ }; +//│ }; +//│ Bad1 = function Bad() { +//│ return (y$11, z$21, x$31, f$capture$01) => { +//│ return new Bad.class()(y$11, z$21, x$31, f$capture$01); +//│ } +//│ }; +//│ Bad1.class = class Bad { +//│ constructor() { +//│ return (y$1, z$2, x$3, f$capture$0) => { +//│ this.y$1 = y$1; +//│ this.z$2 = z$2; +//│ this.x$3 = x$3; +//│ this.f$capture$0 = f$capture$0; +//│ return this; +//│ } +//│ } +//│ foo() { +//│ this.f$capture$0.w0$ = 10000; +//│ return null; +//│ } +//│ toString() { return "Bad(" + "" + ")"; } +//│ }; +//│ f$capture5 = function f$capture(w0$1) { +//│ return new f$capture.class(w0$1); +//│ }; +//│ f$capture5.class = class f$capture4 { +//│ constructor(w0$) { +//│ this.w0$ = w0$; +//│ } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } +//│ }; +//│ f5 = function f() { +//│ let x, y, z, tmp5, tmp6, capture; +//│ capture = new f$capture5(null); +//│ x = 1; +//│ y = 10; +//│ z = 10; +//│ capture.w0$ = 1000; +//│ tmp5 = Bad$(y, z, x, capture); +//│ tmp6 = tmp5.foo() ?? null; +//│ return Good$(y, z, x, capture); +//│ }; +//│ tmp4 = f5(); +//│ tmp4.foo() ?? null +//│ = 10111 + +:fixme +:expect 2 +class A(a) with + let x = 2 + fun f() = + fun g() = x + g() +A(2).f() +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'null' diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls similarity index 60% rename from hkmc2/shared/src/test/mlscript/codegen/Lifter.mls rename to hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 61e876894..eafe20a52 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -252,6 +252,7 @@ x + y //│ x = "0" //│ y = "1" + class Tuple(a, b) fun f(used1) = fun g1(used2) = @@ -431,210 +432,6 @@ g() //│ g13() //│ = 3 -// make sure class fields are discriminated properly for immutable captures when supported -:handler -abstract class Effect with - fun perform() -handle h = Effect with - fun perform()(k) = k() -fun f() = - h.perform() - 1 -f() + f() + f() -//│ = 3 - -/// CLASSES /// - -:expect 1 -:sjs -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - Test(unused1) -f(1, 2).get() -//│ JS (unsanitized): -//│ let f17, Test1, g14, h5, tmp22, Test$ctor, Test$, g$11, h$4; -//│ h$4 = function h$(used3, used1) { -//│ return used3; -//│ }; -//│ h5 = function h(used3, used1) { -//│ return () => { -//│ return h$4(used3, used1); -//│ }; -//│ }; -//│ g$11 = function g$(used1, g_arg) { -//│ let used3, tmp23; -//│ used3 = 2; -//│ tmp23 = h$4(used3, used1); -//│ return used1 + tmp23; -//│ }; -//│ g14 = function g(used1) { -//│ return (g_arg) => { -//│ return g$11(used1, g_arg); -//│ }; -//│ }; -//│ Test$ = function Test$(used1$0, a1) { -//│ let tmp23; -//│ tmp23 = new Test1(a1); -//│ return tmp23(used1$0); -//│ }; -//│ Test$ctor = function Test$ctor(used1$0) { -//│ return (a1) => { -//│ let tmp23; -//│ tmp23 = new Test1(a1); -//│ return tmp23(used1$0); -//│ }; -//│ }; -//│ Test1 = function Test(a2) { -//│ return (used1$01) => { -//│ return new Test.class(a2)(used1$01); -//│ } -//│ }; -//│ Test1.class = class Test { -//│ constructor(a1) { -//│ return (used1$0) => { -//│ this.a = a1; -//│ this.used1$0 = used1$0; -//│ return this; -//│ } -//│ } -//│ get() { -//│ return this.used1$0; -//│ } -//│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } -//│ }; -//│ f17 = function f(used1, unused1) { -//│ let unused2; -//│ unused2 = 2; -//│ return Test$(used1, unused1); -//│ }; -//│ tmp22 = f17(1, 2); -//│ tmp22.get() ?? null -//│ = 1 - -:expect 1 -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - let foo = Test - foo(unused1) -f(1, 2).get() -//│ = 1 - -:fixme -:expect 1 -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - new Test(unused1) -f(1, 2).get() -//│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor -//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' - -:expect 2 -fun f(x) = - class A() with - fun f() = set x = 2 - A().f() - x -f(1) -//│ = 2 - -:expect 6 -class A(a) with - fun f(b) = - fun g(c) = - set b = 2 - a + b + c - g -A(1).f(1)(3) -//│ = 6 - -// Here, g does not need the capture. This should be optimized -:todo -:sjs -:expect 1 -fun f() = - let x = 1 - fun g() = 1 - fun f() = set x = 1 - f() - x -f() -//│ JS (unsanitized): -//│ let f22, f23, g18, f$2, g$15, f$capture17; -//│ g$15 = function g$(f$capture18) { -//│ return 1; -//│ }; -//│ g18 = function g(f$capture18) { -//│ return () => { -//│ return g$15(f$capture18); -//│ }; -//│ }; -//│ f$2 = function f$(f$capture18) { -//│ f$capture18.x0$ = 1; -//│ return null; -//│ }; -//│ f23 = function f(f$capture18) { -//│ return () => { -//│ return f$2(f$capture18); -//│ }; -//│ }; -//│ f$capture17 = function f$capture(x0$1) { -//│ return new f$capture.class(x0$1); -//│ }; -//│ f$capture17.class = class f$capture16 { -//│ constructor(x0$) { -//│ this.x0$ = x0$; -//│ } -//│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } -//│ }; -//│ f22 = function f() { -//│ let tmp27, capture; -//│ capture = new f$capture17(null); -//│ capture.x0$ = 1; -//│ tmp27 = f$2(capture); -//│ return capture.x0$; -//│ }; -//│ f22() -//│ = 1 - -// don't break private variables -:lift -:re -fun foo = () -class A(a) with - foo - let x = 2 - foo -A(2).x -//│ ═══[RUNTIME ERROR] Error: Access to required field 'x' yielded 'undefined' - -:fixme -:expect 2 -class A(a) with - let x = 2 - fun f() = - fun g() = x - g() -A(2).f() -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'null' - :sjs fun g() = let y = 0 @@ -651,26 +448,26 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let g20, f24, h8, tmp29, f$3, h$7, g$capture1; -//│ h$7 = function h$(k, x1, g$capture2) { +//│ let g14, f16, h5, tmp21, f$1, h$4, g$capture1; +//│ h$4 = function h$(k, x1, g$capture2) { //│ k = 5; //│ x1 = 4; //│ return x1 + g$capture2.y0$; //│ }; -//│ h8 = function h(k, x1, g$capture2) { +//│ h5 = function h(k, x1, g$capture2) { //│ return () => { -//│ return h$7(k, x1, g$capture2); +//│ return h$4(k, x1, g$capture2); //│ }; //│ }; -//│ f$3 = function f$(g$capture2, x1) { +//│ f$1 = function f$(g$capture2, x1) { //│ let k; //│ k = 4; //│ g$capture2.y0$ = 2; //│ return x1; //│ }; -//│ f24 = function f(g$capture2) { +//│ f16 = function f(g$capture2) { //│ return (x1) => { -//│ return f$3(g$capture2, x1); +//│ return f$1(g$capture2, x1); //│ }; //│ }; //│ g$capture1 = function g$capture(y0$1) { @@ -682,129 +479,16 @@ g()(1) //│ } //│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } //│ }; -//│ g20 = function g() { +//│ g14 = function g() { //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f24(capture) ?? null; +//│ return f16(capture) ?? null; //│ }; -//│ tmp29 = g20(); -//│ tmp29(1) ?? null +//│ tmp21 = g14(); +//│ tmp21(1) ?? null //│ = 1 -// only w should be in a capture -:expect 10111 -:sjs -fun f() = - let x = 1 - let y = 10 - let z = 10 - let w = 1000 - class Good() with - fun foo() = - set z = 100 - x + y + z + w - class Bad() with - fun foo() = - set w = 10000 - Bad().foo() - Good() -f().foo() -//│ JS (unsanitized): -//│ let f25, Bad1, Good1, tmp30, Bad$ctor, Bad$, Good$ctor, Good$, f$capture19; -//│ Good$ = function Good$(z$1, x$2, y$3, f$capture$0) { -//│ let tmp31; -//│ tmp31 = new Good1(); -//│ return tmp31(z$1, x$2, y$3, f$capture$0); -//│ }; -//│ Good$ctor = function Good$ctor(z$1, x$2, y$3, f$capture$0) { -//│ return () => { -//│ let tmp31; -//│ tmp31 = new Good1(); -//│ return tmp31(z$1, x$2, y$3, f$capture$0); -//│ }; -//│ }; -//│ Good1 = function Good() { -//│ return (z$11, x$21, y$31, f$capture$01) => { -//│ return new Good.class()(z$11, x$21, y$31, f$capture$01); -//│ } -//│ }; -//│ Good1.class = class Good { -//│ constructor() { -//│ return (z$1, x$2, y$3, f$capture$0) => { -//│ this.z$1 = z$1; -//│ this.x$2 = x$2; -//│ this.y$3 = y$3; -//│ this.f$capture$0 = f$capture$0; -//│ return this; -//│ } -//│ } -//│ foo() { -//│ let tmp31, tmp32; -//│ this.z$1 = 100; -//│ tmp31 = this.x$2 + this.y$3; -//│ tmp32 = tmp31 + this.z$1; -//│ return tmp32 + this.f$capture$0.w0$; -//│ } -//│ toString() { return "Good(" + "" + ")"; } -//│ }; -//│ Bad$ = function Bad$(z$1, x$2, y$3, f$capture$0) { -//│ let tmp31; -//│ tmp31 = new Bad1(); -//│ return tmp31(z$1, x$2, y$3, f$capture$0); -//│ }; -//│ Bad$ctor = function Bad$ctor(z$1, x$2, y$3, f$capture$0) { -//│ return () => { -//│ let tmp31; -//│ tmp31 = new Bad1(); -//│ return tmp31(z$1, x$2, y$3, f$capture$0); -//│ }; -//│ }; -//│ Bad1 = function Bad() { -//│ return (z$11, x$21, y$31, f$capture$01) => { -//│ return new Bad.class()(z$11, x$21, y$31, f$capture$01); -//│ } -//│ }; -//│ Bad1.class = class Bad { -//│ constructor() { -//│ return (z$1, x$2, y$3, f$capture$0) => { -//│ this.z$1 = z$1; -//│ this.x$2 = x$2; -//│ this.y$3 = y$3; -//│ this.f$capture$0 = f$capture$0; -//│ return this; -//│ } -//│ } -//│ foo() { -//│ this.f$capture$0.w0$ = 10000; -//│ return null; -//│ } -//│ toString() { return "Bad(" + "" + ")"; } -//│ }; -//│ f$capture19 = function f$capture(w0$1) { -//│ return new f$capture.class(w0$1); -//│ }; -//│ f$capture19.class = class f$capture18 { -//│ constructor(w0$) { -//│ this.w0$ = w0$; -//│ } -//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } -//│ }; -//│ f25 = function f() { -//│ let x1, y1, z, tmp31, tmp32, capture; -//│ capture = new f$capture19(null); -//│ x1 = 1; -//│ y1 = 10; -//│ z = 10; -//│ capture.w0$ = 1000; -//│ tmp31 = Bad$(z, x1, y1, capture); -//│ tmp32 = tmp31.foo() ?? null; -//│ return Good$(z, x1, y1, capture); -//│ }; -//│ tmp30 = f25(); -//│ tmp30.foo() ?? null -//│ = 10111 - :expect 2 fun f() = let b = 0 @@ -846,31 +530,78 @@ fun f(x, cond) = set x = 3 () => x //│ JS (unsanitized): -//│ let f28, lambda1, lambda2, lambda$1, lambda$2; -//│ lambda$2 = function lambda$(x1) { +//│ let f19, lambda, lambda1, lambda$, lambda$1; +//│ lambda$1 = function lambda$(x1) { //│ return x1; //│ }; -//│ lambda1 = function lambda(x1) { +//│ lambda = function lambda(x1) { //│ return () => { -//│ return lambda$2(x1); +//│ return lambda$1(x1); //│ }; //│ }; -//│ lambda$1 = function lambda$(x1) { +//│ lambda$ = function lambda$(x1) { //│ return x1; //│ }; -//│ lambda2 = function lambda(x1) { +//│ lambda1 = function lambda(x1) { //│ return () => { -//│ return lambda$1(x1); +//│ return lambda$(x1); //│ }; //│ }; -//│ f28 = function f(x1, cond) { +//│ f19 = function f(x1, cond) { //│ x1 = 1; //│ x1 = 2; //│ if (cond === true) { -//│ return lambda1(x1) ?? null; +//│ return lambda(x1) ?? null; //│ } else { //│ x1 = 3; -//│ return lambda2(x1) ?? null; +//│ return lambda1(x1) ?? null; //│ } //│ }; //│ null + +:sjs +:expect 1 +fun f() = + let x = 1 + fun g() = 1 + fun f() = set x = 1 + f() + x +f() +//│ JS (unsanitized): +//│ let f20, f22, g17, f$2, g$13, f$capture15; +//│ g$13 = function g$(f$capture16) { +//│ return 1; +//│ }; +//│ g17 = function g(f$capture16) { +//│ return () => { +//│ return g$13(f$capture16); +//│ }; +//│ }; +//│ f$2 = function f$(f$capture16) { +//│ f$capture16.x0$ = 1; +//│ return null; +//│ }; +//│ f22 = function f(f$capture16) { +//│ return () => { +//│ return f$2(f$capture16); +//│ }; +//│ }; +//│ f$capture15 = function f$capture(x0$1) { +//│ return new f$capture.class(x0$1); +//│ }; +//│ f$capture15.class = class f$capture14 { +//│ constructor(x0$) { +//│ this.x0$ = x0$; +//│ } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } +//│ }; +//│ f20 = function f() { +//│ let tmp25, capture; +//│ capture = new f$capture15(null); +//│ capture.x0$ = 1; +//│ tmp25 = f$2(capture); +//│ return capture.x0$; +//│ }; +//│ f20() +//│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls new file mode 100644 index 000000000..d330cc3ab --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInMethod.mls @@ -0,0 +1,12 @@ +:lift +:js + +:expect 6 +class A(a) with + fun f(b) = + fun g(c) = + set b = 2 + a + b + c + g +A(1).f(1)(3) +//│ = 6 From 239b40c76979197a78d0f44425ca321aa2353342 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 17:22:18 +0800 Subject: [PATCH 044/127] Update tests and fix params --- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../scala/hkmc2/codegen/HandlerLowering.scala | 6 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 10 +- .../main/scala/hkmc2/codegen/Lowering.scala | 2 +- .../src/test/mlscript/basics/Inheritance.mls | 4 +- .../src/test/mlscript/handlers/Effects.mls | 484 ++++++++++-------- .../mlscript/handlers/EffectsInClasses.mls | 8 +- .../mlscript/handlers/RecursiveHandlers.mls | 49 +- .../test/mlscript/handlers/StackSafety.mls | 40 +- .../src/test/mlscript/lifter/ClassInFun.mls | 80 +-- .../src/test/mlscript/lifter/FunInFun.mls | 168 +++--- 11 files changed, 463 insertions(+), 390 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 67801879d..e47758a63 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -115,7 +115,7 @@ sealed abstract class Block extends Product with AutoLocated: case AssignField(lhs, nme, rhs, rest) => lhs.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR case Define(defn, rest) => defn.freeVarsLLIR ++ rest.freeVarsLLIR - case HandleBlock(lhs, res, par, cls, hdr, bod, rst) => + case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => (bod.freeVarsLLIR - lhs) ++ rst.freeVarsLLIR ++ hdr.flatMap(_.freeVars) case HandleBlockReturn(res) => res.freeVarsLLIR case End(msg) => Set.empty diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 49c7e4ddd..3e72483e7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -54,7 +54,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val tmp = freshTmp() blockBuilder .assignFieldN(state.res.tail, nextIdent, Call( - state.cls, Value.Lit(Tree.IntLit(state.uid)).asArg :: Nil)(true)) + state.cls, Value.Lit(Tree.IntLit(state.uid)).asArg :: Nil)(true, false)) .assignFieldN(state.res, tailIdent, state.res.tail.next) .ret(state.res)) private val functionHandlerCtx = funcLikeHandlerCtx(N) @@ -382,7 +382,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): transform.applyBlock(b) val handlerBody = translateBlock(prepareBody(h.body), HandlerCtx(false, false, N, state => blockBuilder - .assignFieldN(state.res.tail, nextIdent, PureCall(state.cls, Value.Lit(Tree.IntLit(state.uid)).asArg :: Nil)) + .assignFieldN(state.res.tail, nextIdent, PureCall(state.cls, Value.Lit(Tree.IntLit(state.uid)) :: Nil)) .ret(PureCall(handleBlockImplPath, state.res :: h.lhs.asPath :: Nil)))) val handlers = h.handlers.map: handler => @@ -407,7 +407,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val body = blockBuilder .define(clsDefn) - .assign(h.lhs, Call(clsDefn.sym.asPath, Nil)(true)) + .assign(h.lhs, Call(clsDefn.sym.asPath, Nil)(true, false)) .rest(handlerBody) val defn = FunDefn( diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index c4f5a89e7..bcbb485fa 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -297,7 +297,7 @@ class Lifter(using State): case Some(info) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyArg(_)) - Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun) + Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) case None => super.applyResult(r) // if possible, directly create the bms and replace the result with it case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) @@ -388,7 +388,7 @@ class Lifter(using State): val callSym = info.fakeCtorBms match case Some(v) => v case None => sym - Call(callSym.asPath, getCallArgs(sym, ctx))(false) + Call(callSym.asPath, getCallArgs(sym, ctx))(false, false) // deals with creating parameter lists def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match @@ -459,7 +459,7 @@ class Lifter(using State): val args2 = headPlistCopy.params.map(p => p.sym.asPath.asArg) val bdy = blockBuilder - .ret(Call(singleCallBms.asPath, args1 ++ args2)(true)) // TODO: restParams not considered + .ret(Call(singleCallBms.asPath, args1 ++ args2)(true, false)) // TODO: restParams not considered val mainDefn = FunDefn(f.owner, f.sym, PlainParamList(extraParamsCpy) :: headPlistCopy :: Nil, bdy) val auxDefn = FunDefn(N, singleCallBms, flatPlist, lifted.body) @@ -491,10 +491,10 @@ class Lifter(using State): val inst = Instantiate(c.sym.asPath, paramArgs) var acc = blk => Assign(curSym, inst, blk) for ps <- auxSyms do - val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true) + val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false) curSym = TempSymbol(None, "tmp") acc = blk => acc(Assign(curSym, call, blk)) - val bod = acc(Return(Call(curSym.asPath, extraSyms.map(_.asPath.asArg))(true), false)) + val bod = acc(Return(Call(curSym.asPath, extraSyms.map(_.asPath.asArg))(true, false), false)) inline def toPlist(ls: List[VarSymbol]) = PlainParamList(ls.map(s => Param(FldFlags.empty, s, N))) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index be30c985e..ab57edcb8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -465,7 +465,7 @@ class Lowering(lowerHandlers: Bool, stackLimit: Option[Int], lift: Bool)(using T val pctor = args(as): args => Return(Call(Value.Ref(State.builtinOpsMap("super")), args)(true, true), implct = true) - val clsDef = ClsLikeDefn(N, isym, sym, syntax.Cls, N, S(clsp), + val clsDef = ClsLikeDefn(N, isym, sym, syntax.Cls, N, Nil, S(clsp), mtds, privateFlds, publicFlds, pctor, ctor) Define(clsDef, term_nonTail(New(sym.ref(), Nil, N))(k)) diff --git a/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls b/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls index cc4d1f63d..20c64bdf5 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Inheritance.mls @@ -53,7 +53,9 @@ class Baz(z) extends Bar(z * 1) with fun baz = this.bar * 2 //│ JS (unsanitized): //│ let Baz1; -//│ Baz1 = function Baz(z1) { return new Baz.class(z1); }; +//│ Baz1 = function Baz(z1) { +//│ return new Baz.class(z1); +//│ }; //│ Baz1.class = class Baz extends Bar3.class { //│ constructor(z) { //│ let tmp10; diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 754b5c4a6..ddbc7895c 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -159,7 +159,10 @@ if true do //│ let tmp11, handleBlock$11; //│ handleBlock$11 = function handleBlock$() { //│ let f, h, scrut, tmp12, Cont$24, Handler$h$12; -//│ Handler$h$12 = class Handler$h$11 extends Effect1 { +//│ Handler$h$12 = function Handler$h$() { +//│ return new Handler$h$.class(); +//│ }; +//│ Handler$h$12.class = class Handler$h$11 extends Effect1 { //│ constructor() { //│ let tmp13; //│ tmp13 = super(); @@ -169,10 +172,12 @@ if true do //│ return arg //│ }) //│ } -//│ toString() { return "Handler$h$"; } +//│ toString() { return "Handler$h$(" + "" + ")"; } +//│ }; +//│ h = Handler$h$12(); +//│ Cont$24 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ h = new Handler$h$12(); -//│ Cont$24 = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$24.class = class Cont$23 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp13; @@ -205,7 +210,7 @@ if true do //│ if (scrut === true) { //│ tmp12 = f(); //│ if (tmp12 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp12.tail.next = new Cont$24(0); +//│ tmp12.tail.next = Cont$24(0); //│ return globalThis.Predef.__handleBlockImpl(tmp12, h) //│ } //│ if (tmp12 instanceof globalThis.Predef.__Return.class) { @@ -393,252 +398,287 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ JS (unsanitized): -//│ let tmp19, handleBlock$19; -//│ handleBlock$19 = function handleBlock$() { -//│ let foo1, h, res2, Cont$40, Handler$h$21; -//│ Handler$h$21 = function Handler$h$() { -//│ return new Handler$h$.class(); -//│ }; -//│ Handler$h$21.class = class Handler$h$20 extends Eff1 { -//│ constructor() { -//│ let tmp20; -//│ tmp20 = super(); -//│ } -//│ perform() { -//│ return globalThis.Predef.__mkEffect(this, (k) => { -//│ return k(null) ?? null; -//│ }); -//│ } -//│ toString() { return "Handler$h$(" + "" + ")"; } -//│ }; -//│ h = Handler$h$21(); -//│ Cont$40 = function Cont$(pc1) { +//│ let foo7, tmp21, handleBlock$20; +//│ foo7 = function foo(h) { +//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, Cont$53; +//│ Cont$53 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; -//│ Cont$40.class = class Cont$38 extends globalThis.Predef.__Cont.class { +//│ Cont$53.class = class Cont$50 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp20; -//│ tmp20 = super(null, null); +//│ let tmp29; +//│ tmp29 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { -//│ if (this.pc === 8) { -//│ res2 = value$; +//│ if (this.pc === 0) { +//│ tmp22 = value$; +//│ } else if (this.pc === 2) { +//│ tmp23 = value$; +//│ } else if (this.pc === 1) { +//│ tmp23 = value$; +//│ } else if (this.pc === 4) { +//│ tmp24 = value$; +//│ } else if (this.pc === 3) { +//│ tmp24 = value$; +//│ } else if (this.pc === 5) { +//│ tmp25 = value$; //│ } //│ contLoop: while (true) { -//│ if (this.pc === 8) { -//│ return res2; -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ foo1 = function foo(h1) { -//│ let A8, a, A9, scrut, A10, b, A11, scrut1, A12, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, res3, res4, res5, res6, res7, res8, Cont$41; -//│ Cont$41 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$41.class = class Cont$39 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp27; -//│ tmp27 = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { //│ if (this.pc === 0) { -//│ res3 = value$; -//│ } else if (this.pc === 2) { -//│ res5 = value$; -//│ } else if (this.pc === 1) { -//│ res4 = value$; -//│ } else if (this.pc === 4) { -//│ res7 = value$; -//│ } else if (this.pc === 3) { -//│ res6 = value$; -//│ } else if (this.pc === 5) { -//│ res8 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ tmp20 = res3; -//│ scrut = true; -//│ if (scrut === true) { -//│ res4 = A10.f(); -//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(res4, this); -//│ } +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp23 = A10.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ continue contLoop; -//│ } else { -//│ res5 = A9.f(); -//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 2; -//│ return globalThis.Predef.__appendInCont(res5, this); -//│ } +//│ return globalThis.Predef.__appendInCont(tmp23, this) +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else { +//│ tmp23 = A11.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 2; -//│ continue contLoop; +//│ return globalThis.Predef.__appendInCont(tmp23, this) //│ } -//│ this.pc = 7; +//│ this.pc = 2; //│ continue contLoop; -//│ } else if (this.pc === 7) { -//│ a = tmp21; -//│ scrut1 = false; -//│ if (scrut1 === true) { -//│ res6 = A12.f(); -//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 3; -//│ return globalThis.Predef.__appendInCont(res6, this); -//│ } +//│ } +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 7) { +//│ a = tmp23; +//│ scrut1 = false; +//│ if (scrut1 === true) { +//│ tmp24 = A12.f(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 3; -//│ continue contLoop; -//│ } else { -//│ res7 = A11.f(); -//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 4; -//│ return globalThis.Predef.__appendInCont(res7, this); -//│ } -//│ this.pc = 4; -//│ continue contLoop; +//│ return globalThis.Predef.__appendInCont(tmp24, this) //│ } -//│ this.pc = 6; +//│ this.pc = 3; //│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ tmp21 = res5; -//│ this.pc = 7; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ tmp21 = res4; -//│ this.pc = 7; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ b = tmp22; -//│ res8 = A8(); -//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 5; -//│ return globalThis.Predef.__appendInCont(res8, this); +//│ } else { +//│ tmp24 = A13.f(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 4; +//│ return globalThis.Predef.__appendInCont(tmp24, this) //│ } -//│ this.pc = 5; +//│ this.pc = 4; //│ continue contLoop; -//│ } else if (this.pc === 4) { -//│ tmp22 = res7; -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 3) { -//│ tmp22 = res6; -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 5) { -//│ tmp23 = res8; -//│ tmp24 = tmp23 * 100; -//│ tmp25 = a * 10; -//│ tmp26 = tmp24 + tmp25; -//│ return tmp26 + b; //│ } -//│ break; +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ this.pc = 7; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ b = tmp24; +//│ tmp25 = A14(); +//│ if (tmp25 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 5; +//│ return globalThis.Predef.__appendInCont(tmp25, this) +//│ } +//│ this.pc = 5; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 5) { +//│ tmp26 = tmp25 * 100; +//│ tmp27 = a * 10; +//│ tmp28 = tmp26 + tmp27; +//│ return tmp28 + b //│ } +//│ break; //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ A8 = function A() { -//│ return 1; -//│ }; -//│ A10 = class A4 { -//│ static {} -//│ static f() { -//│ return 2; -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A9 = class A5 { -//│ static {} -//│ static f() { -//│ return 3; -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A12 = class A6 { -//│ static {} -//│ static f() { -//│ return 2; -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A11 = class A7 { -//│ static {} -//│ static f() { -//│ return 3; -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ res3 = h1.perform() ?? null; -//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$41(0); -//│ res3.tail = res3.tail.next; -//│ return res3; //│ } -//│ tmp20 = res3; -//│ scrut = true; -//│ if (scrut === true) { -//│ res4 = A10.f(); -//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = Cont$41(1); -//│ res4.tail = res4.tail.next; -//│ return res4; -//│ } -//│ tmp21 = res4; -//│ } else { -//│ res5 = A9.f(); -//│ if (res5 instanceof globalThis.Predef.__EffectSig.class) { -//│ res5.tail.next = Cont$41(2); -//│ res5.tail = res5.tail.next; -//│ return res5; -//│ } -//│ tmp21 = res5; +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ A14 = function A() { +//│ return 1 +//│ }; +//│ A10 = class A6 { +//│ static {} +//│ static f() { +//│ return 2 +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A11 = class A7 { +//│ static {} +//│ static f() { +//│ return 3 +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A12 = class A8 { +//│ static {} +//│ static f() { +//│ return 2 +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ A13 = class A9 { +//│ static {} +//│ static f() { +//│ return 3 +//│ } +//│ static toString() { return "A"; } +//│ }; +//│ tmp22 = h.perform() ?? null; +//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp22.tail.next = Cont$53(0); +//│ tmp22.tail = tmp22.tail.next; +//│ return tmp22 +//│ } +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp23 = A10.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23.tail.next = Cont$53(1); +//│ tmp23.tail = tmp23.tail.next; +//│ return tmp23 +//│ } +//│ } else { +//│ tmp23 = A11.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23.tail.next = Cont$53(2); +//│ tmp23.tail = tmp23.tail.next; +//│ return tmp23 +//│ } +//│ } +//│ a = tmp23; +//│ scrut1 = false; +//│ if (scrut1 === true) { +//│ tmp24 = A12.f(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp24.tail.next = Cont$53(3); +//│ tmp24.tail = tmp24.tail.next; +//│ return tmp24 +//│ } +//│ } else { +//│ tmp24 = A13.f(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp24.tail.next = Cont$53(4); +//│ tmp24.tail = tmp24.tail.next; +//│ return tmp24 +//│ } +//│ } +//│ b = tmp24; +//│ tmp25 = A14(); +//│ if (tmp25 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp25.tail.next = Cont$53(5); +//│ tmp25.tail = tmp25.tail.next; +//│ return tmp25 +//│ } +//│ tmp26 = tmp25 * 100; +//│ tmp27 = a * 10; +//│ tmp28 = tmp26 + tmp27; +//│ return tmp28 + b +//│ }; +//│ handleBlock$20 = function handleBlock$() { +//│ let h, res3, Cont$53, Handler$h$22; +//│ Handler$h$22 = function Handler$h$() { +//│ return new Handler$h$.class(); +//│ }; +//│ Handler$h$22.class = class Handler$h$21 extends Eff1 { +//│ constructor() { +//│ let tmp22; +//│ tmp22 = super(); +//│ } +//│ perform() { +//│ return globalThis.Predef.__mkEffect(h, (k) => { +//│ let res4, Cont$54; +//│ Cont$54 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$54.class = class Cont$51 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp22; +//│ tmp22 = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 10) { +//│ res4 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 10) { +//│ if (res4 instanceof globalThis.Predef.__Return.class) { +//│ return res4 +//│ } +//│ this.pc = 11; +//│ continue contLoop; +//│ } else if (this.pc === 11) { +//│ return res4 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ res4 = k(null) ?? null; +//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { +//│ res4.tail.next = Cont$54(10); +//│ res4.tail = res4.tail.next; +//│ return res4 +//│ } +//│ if (res4 instanceof globalThis.Predef.__Return.class) { +//│ return res4 +//│ } +//│ return res4 +//│ }) +//│ } +//│ toString() { return "Handler$h$(" + "" + ")"; } +//│ }; +//│ h = Handler$h$22(); +//│ Cont$53 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$53.class = class Cont$52 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp22; +//│ tmp22 = super(null, null); +//│ this.pc = pc; //│ } -//│ a = tmp21; -//│ scrut1 = false; -//│ if (scrut1 === true) { -//│ res6 = A12.f(); -//│ if (res6 instanceof globalThis.Predef.__EffectSig.class) { -//│ res6.tail.next = Cont$41(3); -//│ res6.tail = res6.tail.next; -//│ return res6; +//│ resume(value$) { +//│ if (this.pc === 8) { +//│ res3 = value$; //│ } -//│ tmp22 = res6; -//│ } else { -//│ res7 = A11.f(); -//│ if (res7 instanceof globalThis.Predef.__EffectSig.class) { -//│ res7.tail.next = Cont$41(4); -//│ res7.tail = res7.tail.next; -//│ return res7; +//│ contLoop: while (true) { +//│ if (this.pc === 8) { +//│ if (res3 instanceof globalThis.Predef.__Return.class) { +//│ return res3 +//│ } +//│ this.pc = 9; +//│ continue contLoop; +//│ } else if (this.pc === 9) { +//│ return res3 +//│ } +//│ break; //│ } -//│ tmp22 = res7; //│ } -//│ b = tmp22; -//│ res8 = A8(); -//│ if (res8 instanceof globalThis.Predef.__EffectSig.class) { -//│ res8.tail.next = Cont$41(5); -//│ res8.tail = res8.tail.next; -//│ return res8; -//│ } -//│ tmp23 = res8; -//│ tmp24 = tmp23 * 100; -//│ tmp25 = a * 10; -//│ tmp26 = tmp24 + tmp25; -//│ return tmp26 + b; +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ res2 = foo1(h); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$40(8); -//│ return globalThis.Predef.__handleBlockImpl(res2, h); +//│ res3 = foo7(h); +//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { +//│ res3.tail.next = Cont$53(8); +//│ return globalThis.Predef.__handleBlockImpl(res3, h) +//│ } +//│ if (res3 instanceof globalThis.Predef.__Return.class) { +//│ return res3 //│ } -//│ return res2; +//│ return res3 //│ }; -//│ tmp19 = handleBlock$19(); -//│ if (tmp19 instanceof this.Predef.__EffectSig.class) { +//│ tmp21 = handleBlock$20(); +//│ if (tmp21 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ tmp19 +//│ tmp21 //│ = 123 diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index c26e3a39e..9f4a12333 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -56,13 +56,13 @@ class Lol(h) with //│ }; //│ tmp = this.h.perform("k") ?? null; //│ if (tmp instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp.tail.next = new Cont$1.class(0); +//│ tmp.tail.next = Cont$1(0); //│ tmp.tail = tmp.tail.next; //│ return tmp //│ } //│ res = Predef.print(tmp); //│ if (res instanceof globalThis.Predef.__EffectSig.class) { -//│ res.tail.next = new Cont$1.class(1); +//│ res.tail.next = Cont$1(1); //│ res.tail = res.tail.next; //│ return res //│ } @@ -127,9 +127,9 @@ let oops = Lol(h) //│ > k //│ > b -//│ oops = Lol(Handler$h$) +//│ oops = Lol(Handler$h$()) oops.h -//│ = Handler$h$ +//│ = Handler$h$() diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 7e58769a6..b375d6353 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -47,7 +47,10 @@ handle h2 = Effect with //│ ║ l.38: then h2.perform(arg - 1) + " " + arg //│ ╙── ^^ //│ > ––– -//│ ═══[RUNTIME ERROR] Error: Unhandled effects +//│ > performing 2 +//│ > ––– +//│ > performing 3 +//│ = [undefined, undefined] // The current implementation insert new handlers surrounding the entire handle block, and hence "later" handle block become the outer one @@ -124,7 +127,10 @@ str //│ if (scrut === true) { //│ handleBlock$4 = function handleBlock$() { //│ let h1, tmp8, handleBlock$5, Cont$16, Handler$h1$2; -//│ Handler$h1$2 = class Handler$h1$1 extends Effect1 { +//│ Handler$h1$2 = function Handler$h1$() { +//│ return new Handler$h1$.class(); +//│ }; +//│ Handler$h1$2.class = class Handler$h1$1 extends Effect1 { //│ constructor() { //│ let tmp9; //│ tmp9 = super(); @@ -132,7 +138,9 @@ str //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(h1, (k) => { //│ let tmp9, tmp10, tmp11, Cont$17; -//│ Cont$17 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$17 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$17.class = class Cont$12 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp12; @@ -164,7 +172,7 @@ str //│ str = tmp9; //│ tmp10 = k(arg) ?? null; //│ if (tmp10 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp10.tail.next = new Cont$17.class(8); +//│ tmp10.tail.next = Cont$17(8); //│ tmp10.tail = tmp10.tail.next; //│ return tmp10 //│ } @@ -176,10 +184,12 @@ str //│ return null //│ }) //│ } -//│ toString() { return "Handler$h1$"; } +//│ toString() { return "Handler$h1$(" + "" + ")"; } +//│ }; +//│ h1 = Handler$h1$2(); +//│ Cont$16 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ h1 = new Handler$h1$2(); -//│ Cont$16 = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$16.class = class Cont$13 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp9; @@ -207,7 +217,10 @@ str //│ }; //│ handleBlock$5 = function handleBlock$() { //│ let h2, tmp9, res4, Cont$17, Handler$h2$2; -//│ Handler$h2$2 = class Handler$h2$1 extends Effect1 { +//│ Handler$h2$2 = function Handler$h2$() { +//│ return new Handler$h2$.class(); +//│ }; +//│ Handler$h2$2.class = class Handler$h2$1 extends Effect1 { //│ constructor() { //│ let tmp10; //│ tmp10 = super(); @@ -215,7 +228,9 @@ str //│ perform(arg) { //│ return globalThis.Predef.__mkEffect(h2, (k) => { //│ let tmp10, tmp11, tmp12, tmp13, tmp14, Cont$18; -//│ Cont$18 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$18 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$18.class = class Cont$14 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp15; @@ -249,7 +264,7 @@ str //│ str = tmp11; //│ tmp12 = k(arg) ?? null; //│ if (tmp12 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp12.tail.next = new Cont$18.class(4); +//│ tmp12.tail.next = Cont$18(4); //│ tmp12.tail = tmp12.tail.next; //│ return tmp12 //│ } @@ -262,10 +277,12 @@ str //│ return null //│ }) //│ } -//│ toString() { return "Handler$h2$"; } +//│ toString() { return "Handler$h2$(" + "" + ")"; } +//│ }; +//│ h2 = Handler$h2$2(); +//│ Cont$17 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); //│ }; -//│ h2 = new Handler$h2$2(); -//│ Cont$17 = function Cont$(pc1) { return new Cont$.class(pc1); }; //│ Cont$17.class = class Cont$15 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp10; @@ -309,7 +326,7 @@ str //│ }; //│ tmp9 = h2.perform(null) ?? null; //│ if (tmp9 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp9.tail.next = new Cont$17(0); +//│ tmp9.tail.next = Cont$17(0); //│ return globalThis.Predef.__handleBlockImpl(tmp9, h2) //│ } //│ if (tmp9 instanceof globalThis.Predef.__Return.class) { @@ -317,7 +334,7 @@ str //│ } //│ res4 = h1.perform(null) ?? null; //│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = new Cont$17(1); +//│ res4.tail.next = Cont$17(1); //│ return globalThis.Predef.__handleBlockImpl(res4, h2) //│ } //│ if (res4 instanceof globalThis.Predef.__Return.class) { @@ -327,7 +344,7 @@ str //│ }; //│ tmp8 = handleBlock$5(); //│ if (tmp8 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp8.tail.next = new Cont$16(6); +//│ tmp8.tail.next = Cont$16(6); //│ return globalThis.Predef.__handleBlockImpl(tmp8, h1) //│ } //│ if (tmp8 instanceof globalThis.Predef.__Return.class) { diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index fbb5ed5b0..ea2a8bad8 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -23,7 +23,9 @@ hi(0) //│ let hi, res, handleBlock$; //│ hi = function hi(n) { //│ let scrut, tmp, stackDelayRes, Cont$3; -//│ Cont$3 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$3 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp1; @@ -56,7 +58,7 @@ hi(0) //│ }; //│ stackDelayRes = globalThis.Predef.checkDepth(); //│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { -//│ stackDelayRes.tail.next = new Cont$3.class(0); +//│ stackDelayRes.tail.next = Cont$3(0); //│ stackDelayRes.tail = stackDelayRes.tail.next; //│ return stackDelayRes //│ } @@ -71,7 +73,10 @@ hi(0) //│ }; //│ handleBlock$ = function handleBlock$() { //│ let stackHandler, res1, Cont$3, StackDelay$1; -//│ StackDelay$1 = class StackDelay$ extends globalThis.Predef.__StackDelay { +//│ StackDelay$1 = function StackDelay$() { +//│ return new StackDelay$.class(); +//│ }; +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { //│ constructor() { //│ let tmp; //│ tmp = super(); @@ -79,7 +84,9 @@ hi(0) //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { //│ let res2, Cont$4; -//│ Cont$4 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$4 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; @@ -108,7 +115,7 @@ hi(0) //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res2 = resume(); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = new Cont$4.class(4); +//│ res2.tail.next = Cont$4(4); //│ res2.tail = res2.tail.next; //│ return res2 //│ } @@ -155,7 +162,7 @@ hi(0) //│ globalThis.Predef.__stackHandler = stackHandler; //│ res1 = hi(0); //│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = new Cont$3(2); +//│ res1.tail.next = Cont$3(2); //│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler) //│ } //│ if (res1 instanceof globalThis.Predef.__Return.class) { @@ -185,7 +192,9 @@ sum(10000) //│ let sum1, res1, handleBlock$1; //│ sum1 = function sum(n) { //│ let scrut, tmp, tmp1, curDepth, stackDelayRes, Cont$6; -//│ Cont$6 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$6 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$6.class = class Cont$3 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp2; @@ -230,7 +239,7 @@ sum(10000) //│ curDepth = globalThis.Predef.__stackDepth; //│ stackDelayRes = globalThis.Predef.checkDepth(); //│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { -//│ stackDelayRes.tail.next = new Cont$6.class(0); +//│ stackDelayRes.tail.next = Cont$6(0); //│ stackDelayRes.tail = stackDelayRes.tail.next; //│ return stackDelayRes //│ } @@ -242,7 +251,7 @@ sum(10000) //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; //│ tmp1 = sum1(tmp); //│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp1.tail.next = new Cont$6.class(1); +//│ tmp1.tail.next = Cont$6(1); //│ tmp1.tail = tmp1.tail.next; //│ return tmp1 //│ } @@ -252,7 +261,10 @@ sum(10000) //│ }; //│ handleBlock$1 = function handleBlock$() { //│ let stackHandler, res2, Cont$6, StackDelay$2; -//│ StackDelay$2 = class StackDelay$1 extends globalThis.Predef.__StackDelay { +//│ StackDelay$2 = function StackDelay$() { +//│ return new StackDelay$.class(); +//│ }; +//│ StackDelay$2.class = class StackDelay$1 extends globalThis.Predef.__StackDelay { //│ constructor() { //│ let tmp; //│ tmp = super(); @@ -260,7 +272,9 @@ sum(10000) //│ perform() { //│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { //│ let res3, Cont$7; -//│ Cont$7 = function Cont$(pc1) { return new Cont$.class(pc1); }; +//│ Cont$7 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; //│ Cont$7.class = class Cont$4 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { //│ let tmp; @@ -289,7 +303,7 @@ sum(10000) //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; //│ res3 = resume(); //│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = new Cont$7.class(5); +//│ res3.tail.next = Cont$7(5); //│ res3.tail = res3.tail.next; //│ return res3 //│ } @@ -336,7 +350,7 @@ sum(10000) //│ globalThis.Predef.__stackHandler = stackHandler; //│ res2 = sum1(10000); //│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = new Cont$6(3); +//│ res2.tail.next = Cont$6(3); //│ return globalThis.Predef.__handleBlockImpl(res2, stackHandler) //│ } //│ if (res2 instanceof globalThis.Predef.__Return.class) { diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index aad5be577..9baad4a87 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -26,37 +26,37 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let f1, Test1, g, h, tmp1, Test$ctor, Test$, g$, h$; +//│ let h, Test1, g, f1, tmp1, Test$ctor, Test$, g$, h$; //│ h$ = function h$(used3, used1) { -//│ return used3; +//│ return used3 //│ }; //│ h = function h(used3, used1) { //│ return () => { -//│ return h$(used3, used1); -//│ }; +//│ return h$(used3, used1) +//│ } //│ }; //│ g$ = function g$(used1, g_arg) { //│ let used3, tmp2; //│ used3 = 2; //│ tmp2 = h$(used3, used1); -//│ return used1 + tmp2; +//│ return used1 + tmp2 //│ }; //│ g = function g(used1) { //│ return (g_arg) => { -//│ return g$(used1, g_arg); -//│ }; +//│ return g$(used1, g_arg) +//│ } //│ }; //│ Test$ = function Test$(used1$0, a) { //│ let tmp2; //│ tmp2 = new Test1(a); -//│ return tmp2(used1$0); +//│ return tmp2(used1$0) //│ }; //│ Test$ctor = function Test$ctor(used1$0) { //│ return (a) => { //│ let tmp2; //│ tmp2 = new Test1(a); -//│ return tmp2(used1$0); -//│ }; +//│ return tmp2(used1$0) +//│ } //│ }; //│ Test1 = function Test(a1) { //│ return (used1$01) => { @@ -72,14 +72,14 @@ f(1, 2).get() //│ } //│ } //│ get() { -//│ return this.used1$0; +//│ return this.used1$0 //│ } //│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; //│ f1 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; -//│ return Test$(used1, unused1); +//│ return Test$(used1, unused1) //│ }; //│ tmp1 = f1(1, 2); //│ tmp1.get() ?? null @@ -142,29 +142,29 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let f5, Bad1, Good1, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; -//│ Good$ = function Good$(y$1, z$2, x$3, f$capture$0) { +//│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; +//│ Good$ = function Good$(z$1, y$2, x$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ return tmp5(z$1, y$2, x$3, f$capture$0) //│ }; -//│ Good$ctor = function Good$ctor(y$1, z$2, x$3, f$capture$0) { +//│ Good$ctor = function Good$ctor(z$1, y$2, x$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(y$1, z$2, x$3, f$capture$0); -//│ }; +//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ } //│ }; //│ Good1 = function Good() { -//│ return (y$11, z$21, x$31, f$capture$01) => { -//│ return new Good.class()(y$11, z$21, x$31, f$capture$01); +//│ return (z$11, y$21, x$31, f$capture$01) => { +//│ return new Good.class()(z$11, y$21, x$31, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (y$1, z$2, x$3, f$capture$0) => { -//│ this.y$1 = y$1; -//│ this.z$2 = z$2; +//│ return (z$1, y$2, x$3, f$capture$0) => { +//│ this.z$1 = z$1; +//│ this.y$2 = y$2; //│ this.x$3 = x$3; //│ this.f$capture$0 = f$capture$0; //│ return this; @@ -172,35 +172,35 @@ f().foo() //│ } //│ foo() { //│ let tmp5, tmp6; -//│ this.z$2 = 100; -//│ tmp5 = this.x$3 + this.y$1; -//│ tmp6 = tmp5 + this.z$2; -//│ return tmp6 + this.f$capture$0.w0$; +//│ this.z$1 = 100; +//│ tmp5 = this.x$3 + this.y$2; +//│ tmp6 = tmp5 + this.z$1; +//│ return tmp6 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; -//│ Bad$ = function Bad$(y$1, z$2, x$3, f$capture$0) { +//│ Bad$ = function Bad$(z$1, y$2, x$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(y$1, z$2, x$3, f$capture$0); +//│ return tmp5(z$1, y$2, x$3, f$capture$0) //│ }; -//│ Bad$ctor = function Bad$ctor(y$1, z$2, x$3, f$capture$0) { +//│ Bad$ctor = function Bad$ctor(z$1, y$2, x$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(y$1, z$2, x$3, f$capture$0); -//│ }; +//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ } //│ }; //│ Bad1 = function Bad() { -//│ return (y$11, z$21, x$31, f$capture$01) => { -//│ return new Bad.class()(y$11, z$21, x$31, f$capture$01); +//│ return (z$11, y$21, x$31, f$capture$01) => { +//│ return new Bad.class()(z$11, y$21, x$31, f$capture$01); //│ } //│ }; //│ Bad1.class = class Bad { //│ constructor() { -//│ return (y$1, z$2, x$3, f$capture$0) => { -//│ this.y$1 = y$1; -//│ this.z$2 = z$2; +//│ return (z$1, y$2, x$3, f$capture$0) => { +//│ this.z$1 = z$1; +//│ this.y$2 = y$2; //│ this.x$3 = x$3; //│ this.f$capture$0 = f$capture$0; //│ return this; @@ -208,7 +208,7 @@ f().foo() //│ } //│ foo() { //│ this.f$capture$0.w0$ = 10000; -//│ return null; +//│ return null //│ } //│ toString() { return "Bad(" + "" + ")"; } //│ }; @@ -228,9 +228,9 @@ f().foo() //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(y, z, x, capture); +//│ tmp5 = Bad$(z, y, x, capture); //│ tmp6 = tmp5.foo() ?? null; -//│ return Good$(y, z, x, capture); +//│ return Good$(z, y, x, capture) //│ }; //│ tmp4 = f5(); //│ tmp4.foo() ?? null diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index eafe20a52..9d40a496a 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -12,23 +12,23 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f, g, g$; +//│ let g, f, g$; //│ g$ = function g$(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; -//│ return used1 + used2; +//│ return used1 + used2 //│ }; //│ g = function g(used1, used2) { //│ return (g_arg) => { -//│ return g$(used1, used2, g_arg); -//│ }; +//│ return g$(used1, used2, g_arg) +//│ } //│ }; //│ f = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; //│ tmp = g$(used1, used2, used2); -//│ return tmp + unused2; +//│ return tmp + unused2 //│ }; //│ f(1, 2) //│ = 5 @@ -44,20 +44,20 @@ fun f(used1, unused1) = g(used2)(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f1, g1, g$1; +//│ let g1, f1, g$1; //│ g$1 = function g$(used2, used1, g_arg) { //│ return (g_arg2) => { //│ let used3, tmp, tmp1; //│ used3 = 2; //│ tmp = used1 + used2; //│ tmp1 = tmp + g_arg; -//│ return tmp1 + g_arg2; -//│ }; +//│ return tmp1 + g_arg2 +//│ } //│ }; //│ g1 = function g(used2, used1) { //│ return (g_arg) => { -//│ return g$1(used2, used1, g_arg); -//│ }; +//│ return g$1(used2, used1, g_arg) +//│ } //│ }; //│ f1 = function f(used1, unused1) { //│ let used2, unused2, tmp, tmp1; @@ -65,7 +65,7 @@ f(1, 2) //│ unused2 = 2; //│ tmp = g$1(used2, used1, used2); //│ tmp1 = tmp(used2) ?? null; -//│ return tmp1 + unused2; +//│ return tmp1 + unused2 //│ }; //│ f1(1, 2) //│ = 9 @@ -94,16 +94,16 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f3, g3, g$3; +//│ let g3, f3, g$3; //│ g$3 = function g$(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; -//│ return used1 + used2; +//│ return used1 + used2 //│ }; //│ g3 = function g(used1, used2) { //│ return (g_arg) => { -//│ return g$3(used1, used2, g_arg); -//│ }; +//│ return g$3(used1, used2, g_arg) +//│ } //│ }; //│ f3 = function f(used1, unused1) { //│ let used2, unused2, foo, tmp; @@ -111,7 +111,7 @@ f(1, 2) //│ unused2 = 2; //│ foo = g3(used1, used2) ?? null; //│ tmp = foo(used2) ?? null; -//│ return tmp + unused2; +//│ return tmp + unused2 //│ }; //│ f3(1, 2) //│ = 5 @@ -126,16 +126,16 @@ fun f(used1, unused1) = g(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let f4, g4; +//│ let g4, f4; //│ g4 = function g(g_arg) { -//│ return g_arg + 1; +//│ return g_arg + 1 //│ }; //│ f4 = function f(used1, unused1) { //│ let used2, unused2, tmp; //│ used2 = unused1; //│ unused2 = 2; //│ tmp = g4(used2); -//│ return tmp + unused2; +//│ return tmp + unused2 //│ }; //│ f4(1, 2) //│ = 5 @@ -150,24 +150,24 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f5, g5, g$4; -//│ g$4 = function g$(a5, a1, a4, a3, a6, a2) { +//│ let g5, f5, g$4; +//│ g$4 = function g$(a3, a4, a5, a1, a6, a2) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; //│ tmp2 = tmp1 + a4; //│ tmp3 = tmp2 + a5; -//│ return tmp3 + a6; +//│ return tmp3 + a6 //│ }; -//│ g5 = function g(a5, a1, a4, a3, a6, a2) { +//│ g5 = function g(a3, a4, a5, a1, a6, a2) { //│ return () => { -//│ return g$4(a5, a1, a4, a3, a6, a2); -//│ }; +//│ return g$4(a3, a4, a5, a1, a6, a2) +//│ } //│ }; //│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$4(a5, a1, a4, a3, a6, a2); -//│ return tmp; +//│ tmp = g$4(a3, a4, a5, a1, a6, a2); +//│ return tmp //│ }; //│ f5(1, 2, 3, 4, 5, 6) //│ = 21 @@ -188,23 +188,23 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let f6, Tuple1, g6, h, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; +//│ let g6, h, f6, Tuple1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; //│ g$5 = function g$(f$capture2) { //│ f$capture2.a0$ = 1; -//│ return null; +//│ return null //│ }; //│ g6 = function g(f$capture2) { //│ return () => { -//│ return g$5(f$capture2); -//│ }; +//│ return g$5(f$capture2) +//│ } //│ }; //│ h$ = function h$(f$capture2) { -//│ return f$capture2.a0$; +//│ return f$capture2.a0$ //│ }; //│ h = function h(f$capture2) { //│ return () => { -//│ return h$(f$capture2); -//│ }; +//│ return h$(f$capture2) +//│ } //│ }; //│ f$capture1 = function f$capture(a0$1) { //│ return new f$capture.class(a0$1); @@ -221,7 +221,7 @@ x + y //│ capture.a0$ = 0; //│ g$this = g6(capture) ?? null; //│ h$this = h(capture) ?? null; -//│ return Tuple1(g$this, h$this); +//│ return Tuple1(g$this, h$this) //│ }; //│ Tuple1 = function Tuple(a1, b1) { //│ return new Tuple.class(a1, b1); @@ -299,23 +299,23 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f8, g7, h2, h$2, g$6, f$capture5; +//│ let g7, h2, f8, h$2, g$6, f$capture5; //│ g$6 = function g$(immutable, f$capture6) { //│ f$capture6.mutated0$ = 2; -//│ return immutable + f$capture6.mutated0$; +//│ return immutable + f$capture6.mutated0$ //│ }; //│ g7 = function g(immutable, f$capture6) { //│ return () => { -//│ return g$6(immutable, f$capture6); -//│ }; +//│ return g$6(immutable, f$capture6) +//│ } //│ }; //│ h$2 = function h$(immutable, f$capture6) { -//│ return f$capture6.mutated0$; +//│ return f$capture6.mutated0$ //│ }; //│ h2 = function h(immutable, f$capture6) { //│ return () => { -//│ return h$2(immutable, f$capture6); -//│ }; +//│ return h$2(immutable, f$capture6) +//│ } //│ }; //│ f$capture5 = function f$capture(mutated0$1) { //│ return new f$capture.class(mutated0$1); @@ -333,7 +333,7 @@ f(1, 2, 1000) //│ a1 = tmp21; //│ tmp22 = h$2(immutable, capture); //│ tmp23 = a1 + tmp22; -//│ return tmp23 + unused; +//│ return tmp23 + unused //│ }; //│ f8(1, 2, 1000) //│ = 7 @@ -366,39 +366,39 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let f12, g10, h3, h$3, g$9; +//│ let g10, h3, f12, h$3, g$9; //│ g$9 = function g$(arg, n) { //│ let scrut, tmp21; //│ scrut = n <= 0; //│ if (scrut === true) { -//│ return arg; +//│ return arg //│ } else { //│ tmp21 = n - 1; -//│ return h$3(arg, tmp21); +//│ return h$3(arg, tmp21) //│ } //│ }; //│ g10 = function g(arg) { //│ return (n) => { -//│ return g$9(arg, n); -//│ }; +//│ return g$9(arg, n) +//│ } //│ }; //│ h$3 = function h$(arg, n) { //│ let scrut, tmp21; //│ scrut = n <= 0; //│ if (scrut === true) { -//│ return arg; +//│ return arg //│ } else { //│ tmp21 = n - 1; -//│ return g$9(arg, tmp21); +//│ return g$9(arg, tmp21) //│ } //│ }; //│ h3 = function h(arg) { //│ return (n) => { -//│ return h$3(arg, n); -//│ }; +//│ return h$3(arg, n) +//│ } //│ }; //│ f12 = function f(arg) { -//│ return h$3(arg, 5); +//│ return h$3(arg, 5) //│ }; //│ f12(2) //│ = 2 @@ -419,15 +419,15 @@ fun g() = h() g() //│ JS (unsanitized): -//│ let f15, g13, h4; +//│ let h4, f15, g13; //│ f15 = function f() { -//│ return 3; +//│ return 3 //│ }; //│ h4 = function h() { -//│ return 3; +//│ return 3 //│ }; //│ g13 = function g() { -//│ return h4(); +//│ return h4() //│ }; //│ g13() //│ = 3 @@ -448,27 +448,27 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let g14, f16, h5, tmp21, f$1, h$4, g$capture1; +//│ let h5, f16, g14, tmp21, f$1, h$4, g$capture1; //│ h$4 = function h$(k, x1, g$capture2) { //│ k = 5; //│ x1 = 4; -//│ return x1 + g$capture2.y0$; +//│ return x1 + g$capture2.y0$ //│ }; //│ h5 = function h(k, x1, g$capture2) { //│ return () => { -//│ return h$4(k, x1, g$capture2); -//│ }; +//│ return h$4(k, x1, g$capture2) +//│ } //│ }; //│ f$1 = function f$(g$capture2, x1) { //│ let k; //│ k = 4; //│ g$capture2.y0$ = 2; -//│ return x1; +//│ return x1 //│ }; //│ f16 = function f(g$capture2) { //│ return (x1) => { -//│ return f$1(g$capture2, x1); -//│ }; +//│ return f$1(g$capture2, x1) +//│ } //│ }; //│ g$capture1 = function g$capture(y0$1) { //│ return new g$capture.class(y0$1); @@ -483,7 +483,7 @@ g()(1) //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f16(capture) ?? null; +//│ return f16(capture) ?? null //│ }; //│ tmp21 = g14(); //│ tmp21(1) ?? null @@ -532,29 +532,29 @@ fun f(x, cond) = //│ JS (unsanitized): //│ let f19, lambda, lambda1, lambda$, lambda$1; //│ lambda$1 = function lambda$(x1) { -//│ return x1; +//│ return x1 //│ }; //│ lambda = function lambda(x1) { //│ return () => { -//│ return lambda$1(x1); -//│ }; +//│ return lambda$1(x1) +//│ } //│ }; //│ lambda$ = function lambda$(x1) { -//│ return x1; +//│ return x1 //│ }; //│ lambda1 = function lambda(x1) { //│ return () => { -//│ return lambda$(x1); -//│ }; +//│ return lambda$(x1) +//│ } //│ }; //│ f19 = function f(x1, cond) { //│ x1 = 1; //│ x1 = 2; //│ if (cond === true) { -//│ return lambda(x1) ?? null; +//│ return lambda(x1) ?? null //│ } else { //│ x1 = 3; -//│ return lambda1(x1) ?? null; +//│ return lambda1(x1) ?? null //│ } //│ }; //│ null @@ -569,23 +569,23 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f20, f22, g17, f$2, g$13, f$capture15; +//│ let f20, g17, f22, f$2, g$13, f$capture15; //│ g$13 = function g$(f$capture16) { -//│ return 1; +//│ return 1 //│ }; //│ g17 = function g(f$capture16) { //│ return () => { -//│ return g$13(f$capture16); -//│ }; +//│ return g$13(f$capture16) +//│ } //│ }; //│ f$2 = function f$(f$capture16) { //│ f$capture16.x0$ = 1; -//│ return null; +//│ return null //│ }; -//│ f22 = function f(f$capture16) { +//│ f20 = function f(f$capture16) { //│ return () => { -//│ return f$2(f$capture16); -//│ }; +//│ return f$2(f$capture16) +//│ } //│ }; //│ f$capture15 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); @@ -596,12 +596,12 @@ f() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f20 = function f() { +//│ f22 = function f() { //│ let tmp25, capture; //│ capture = new f$capture15(null); //│ capture.x0$ = 1; //│ tmp25 = f$2(capture); -//│ return capture.x0$; +//│ return capture.x0$ //│ }; -//│ f20() +//│ f22() //│ = 1 From b69a8cb0d63c4ace9d797ee8c7cf16cd75f0db0c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 17:26:37 +0800 Subject: [PATCH 045/127] fix some things --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 2 ++ hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index e47758a63..65a7119a8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -55,6 +55,7 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, fin, rst) => 1 + sub.size + fin.size + rst.size case Label(_, bod, rst) => 1 + bod.size + rst.size case HandleBlock(lhs, res, par, args, cls, handlers, bdy, rst) => 1 + handlers.map(_.body.size).sum + bdy.size + rst.size + case AssignDynField(lhs, fld, arrayIdx, rhs, rst) => 1 + rst.size // TODO conserve if no changes def mapTail(f: BlockTail => Block): Block = this match @@ -90,6 +91,7 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, finallyDo, rest) => sub.freeVars ++ finallyDo.freeVars ++ rest.freeVars case Assign(lhs, rhs, rest) => Set(lhs) ++ rhs.freeVars ++ rest.freeVars case AssignField(lhs, nme, rhs, rest) => lhs.freeVars ++ rhs.freeVars ++ rest.freeVars + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR case Define(defn, rest) => defn.freeVars ++ rest.freeVars case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => (bod.freeVars - lhs) ++ rst.freeVars ++ hdr.flatMap(_.freeVars) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index bcbb485fa..537fcc399 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -198,7 +198,7 @@ class Lifter(using State): val reqdVars: List[Local], val reqdInnerSyms: List[InnerSymbol], val fakeCtorBms: Option[BlockMemberSymbol], // only for classes - val singleCallBms: BlockMemberSymbol // optimization + val singleCallBms: BlockMemberSymbol, // optimization ) case class Lifted[+T <: Defn]( From 688187ab598ebb2735a7ffc204102b0142846eb4 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 18:04:04 +0800 Subject: [PATCH 046/127] refactor some things --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 537fcc399..9c45f3f8e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -672,7 +672,7 @@ class UsedVarAnalyzer: fVars private val blkMutCache: MutMap[Local, Set[Local]] = MutMap.empty - private def findBlkMutations(b: Block, cacheId: Opt[Local] = N): Set[Local] = + private def blkMutationsShallow(b: Block, cacheId: Opt[Local] = N): Set[Local] = cacheId.flatMap(blkMutCache.get) match case Some(value) => value case None => @@ -683,12 +683,12 @@ class UsedVarAnalyzer: mutated += lhs applyBlock(rest) case Label(label, body, rest) => - mutated ++= findBlkMutations(body, S(label)) + mutated ++= blkMutationsShallow(body, S(label)) applyBlock(rest) case _ => super.applyBlock(b) override def applyDefn(defn: Defn): Defn = - mutated ++= findMutations(defn) + mutated ++= findMutationsShallow(defn) super.applyDefn(defn) walker.applyBlock(b) @@ -702,19 +702,20 @@ class UsedVarAnalyzer: private val mutatedCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty /** - * Finds the variables which this definition could possibly mutate. + * Finds the variables which this definition could possibly mutate, excluding mutations through + * calls to other functions. * - * @param defn The definition to search through + * @param defn The definition to search through. * @return The variables which this definition could possibly mutate. */ - private def findMutations(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match + private def findMutationsShallow(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match case Some(value) => value case None => val ret = defn match case f: FunDefn => - findBlkMutations(f.body) + blkMutationsShallow(f.body) case c: ClsLikeDefn => - findBlkMutations(c.preCtor) ++ findBlkMutations(c.ctor) ++ c.methods.flatMap(findMutations) + blkMutationsShallow(c.preCtor) ++ blkMutationsShallow(c.ctor) ++ c.methods.flatMap(findMutationsShallow) case _: ValDefn => Set.empty mutatedCache.addOne(defn.sym -> ret) ret @@ -771,7 +772,7 @@ class UsedVarAnalyzer: // See the above TODO val c @ CaptureInfo(req, read, mut) = rec(body) merge(c) - reqCapture ++= read.intersect(findBlkMutations(body, S(label))) + reqCapture ++= read.intersect(blkMutationsShallow(body, S(label))) reqCapture ++= mut.intersect(body.freeVars) applyBlock(rest) b @@ -797,7 +798,7 @@ class UsedVarAnalyzer: defnSyms.get(l) match case None => super.applyValue(v) case Some(defn) => - val muts = findMutations(defn).intersect(thisVars) + val muts = findMutationsShallow(defn).intersect(thisVars) val reads = defn.freeVars.intersect(thisVars) -- muts // functions mutating a variable will always need a capture for l <- muts do @@ -824,7 +825,7 @@ class UsedVarAnalyzer: // 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 - def findUsedLocalsImpl(f: FunDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = + def findUsedLocalsFn(f: FunDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = val thisVars = Lifter.getVars(f) -- existing val newExisting = existing ++ thisVars @@ -835,16 +836,21 @@ class UsedVarAnalyzer: val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = defn match case f: FunDefn => - usedMap ++= findUsedLocalsImpl(f, newExisting) + usedMap ++= findUsedLocalsFn(f, newExisting) f case c: ClsLikeDefn => val newNewExisting = newExisting ++ c.preCtor.definedVars ++ c.ctor.definedVars for f <- c.methods do - usedMap ++= findUsedLocalsImpl(f, newNewExisting) + usedMap ++= findUsedLocalsFn(f, newNewExisting) c case d => super.applyDefn(d) walker.applyBlock(f.body) usedMap + + def findUsedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = + val newExisting = existing ++ c.preCtor.definedVars ++ c.ctor.definedVars + c.methods.foldLeft(Map.empty): + case (acc, f) => acc ++ findUsedLocalsFn(f, newExisting) /** * Finds the used locals of functions which have been used by their nested definitions. @@ -857,12 +863,10 @@ class UsedVarAnalyzer: val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = defn match case f: FunDefn => - usedMap ++= findUsedLocalsImpl(f, b.definedVars) + usedMap ++= findUsedLocalsFn(f, b.definedVars) f case c: ClsLikeDefn => - val newNewExisting = b.definedVars ++ c.preCtor.definedVars ++ c.ctor.definedVars - for f <- c.methods do - usedMap ++= findUsedLocalsImpl(f, newNewExisting) + usedMap ++= findUsedLocalsCls(c, b.definedVars) c case d => super.applyDefn(d) walker.applyBlock(b) From 9a9229a71c5a409df99499b292cd050cf4f10aaf Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 18:06:20 +0800 Subject: [PATCH 047/127] fix newline --- hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala index 5cbe5e4e3..b874955d1 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala @@ -53,4 +53,3 @@ class CompileTestRunner .map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) end CompileTestRunner - From e49cdaa51e21ef8d3e6978d7f96be0a369859390 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 18:06:39 +0800 Subject: [PATCH 048/127] fix newline --- hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala index b874955d1..8c4e0a855 100644 --- a/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala +++ b/hkmc2/jvm/src/test/scala/hkmc2/CompileTestRunner.scala @@ -53,3 +53,5 @@ class CompileTestRunner .map("\n\t"+relativeName+"."+file.ext+":"+_).mkString(", ")) end CompileTestRunner + + From 0fcdd5c8001b7ef0056beba711fad9b99ff70825 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 18:07:37 +0800 Subject: [PATCH 049/127] fix whitespace --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 65a7119a8..333f39549 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -310,7 +310,7 @@ sealed abstract class Result: case Value.Lam(params, body) => body :: Nil case Value.Arr(elems) => elems.flatMap(_.value.subBlocks) case _ => Nil - + lazy val freeVars: Set[Local] = this match case Call(fun, args) => fun.freeVars ++ args.flatMap(_.value.freeVars).toSet case Instantiate(cls, args) => cls.freeVars ++ args.flatMap(_.freeVars).toSet From b025916e2d0c5e0d690d798eb3e414d0a34766e8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 4 Feb 2025 18:13:35 +0800 Subject: [PATCH 050/127] fix whitespace --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 333f39549..07e455151 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -138,7 +138,7 @@ sealed abstract class Block extends Product with AutoLocated: case Throw(r) => r.subBlocks case _: Return | _: Throw | _: Label | _: Break | _: Continue | _: End | _: HandleBlockReturn => Nil - + // Moves definitions in a block to the top. Only scans the top-level definitions of the block; // i.e, definitions inside other definitions are not moved out. Definitions inside `match`/`if` // and `while` statements are moved out. @@ -219,7 +219,7 @@ sealed abstract class Defn: case _: ValDefn => Nil case ClsLikeDefn(preCtor = preCtor, ctor = ctor, methods = mtds) => preCtor :: ctor :: mtds.flatMap(_.subBlocks) - + lazy val freeVars: Set[Local] = this match case FunDefn(own, sym, params, body) => body.freeVars -- params.flatMap(_.paramSyms) - sym case ValDefn(owner, k, sym, rhs) => rhs.freeVars @@ -310,7 +310,7 @@ sealed abstract class Result: case Value.Lam(params, body) => body :: Nil case Value.Arr(elems) => elems.flatMap(_.value.subBlocks) case _ => Nil - + lazy val freeVars: Set[Local] = this match case Call(fun, args) => fun.freeVars ++ args.flatMap(_.value.freeVars).toSet case Instantiate(cls, args) => cls.freeVars ++ args.flatMap(_.freeVars).toSet From ae64a5de918aa4dadbf1c8c8537b2b4fe3ace2b5 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 8 Feb 2025 16:27:28 +0800 Subject: [PATCH 051/127] update tests --- .../src/test/mlscript/codegen/Modules.mls | 2 +- .../src/test/mlscript/codegen/RandomStuff.mls | 4 +- .../src/test/mlscript/handlers/Effects.mls | 154 +++++++++--------- .../src/test/mlscript/lifter/ClassInFun.mls | 53 +++--- .../src/test/mlscript/lifter/FunInFun.mls | 73 ++++----- 5 files changed, 143 insertions(+), 143 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index 5747f48bc..69c64f615 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -77,7 +77,7 @@ M.y :re M.oops //│ ╔══[ERROR] Module 'M' does not contain member 'oops' -//│ ║ l.76: M.oops +//│ ║ l.78: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls index 39672b85d..a234810db 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls @@ -67,10 +67,10 @@ do let foo = 1 fun foo(x) = foo //│ ╔══[ERROR] Name 'foo' is already used -//│ ║ l.65: let foo = 1 +//│ ║ l.67: let foo = 1 //│ ║ ^^^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.66: fun foo(x) = foo +//│ ║ l.68: fun foo(x) = foo //│ ╙── ^^^^^^^^^^^^ //│ JS (unsanitized): //│ let foo3, foo4; foo3 = function foo(x) { return foo4 }; foo4 = 1; diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index d0ad9ca02..3ed2ea5fc 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -398,48 +398,48 @@ handle h = Eff with fun perform()(k) = k(()) foo(h) //│ JS (unsanitized): -//│ let foo7, tmp21, handleBlock$20; +//│ let foo7, tmp20, handleBlock$20; //│ foo7 = function foo(h) { -//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, Cont$53; +//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, Cont$53; //│ Cont$53 = function Cont$(pc1) { //│ return new Cont$.class(pc1); //│ }; //│ Cont$53.class = class Cont$50 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp29; -//│ tmp29 = super(null, null); +//│ let tmp28; +//│ tmp28 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ tmp22 = value$; +//│ tmp21 = value$; //│ } else if (this.pc === 2) { -//│ tmp23 = value$; +//│ tmp22 = value$; //│ } else if (this.pc === 1) { -//│ tmp23 = value$; +//│ tmp22 = value$; //│ } else if (this.pc === 4) { -//│ tmp24 = value$; +//│ tmp23 = value$; //│ } else if (this.pc === 3) { -//│ tmp24 = value$; +//│ tmp23 = value$; //│ } else if (this.pc === 5) { -//│ tmp25 = value$; +//│ tmp24 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { //│ scrut = true; //│ if (scrut === true) { -//│ tmp23 = A10.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp22 = A10.f(); +//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(tmp23, this) +//│ return globalThis.Predef.__appendInCont(tmp22, this) //│ } //│ this.pc = 1; //│ continue contLoop; //│ } else { -//│ tmp23 = A11.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp22 = A11.f(); +//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 2; -//│ return globalThis.Predef.__appendInCont(tmp23, this) +//│ return globalThis.Predef.__appendInCont(tmp22, this) //│ } //│ this.pc = 2; //│ continue contLoop; @@ -447,21 +447,21 @@ foo(h) //│ this.pc = 7; //│ continue contLoop; //│ } else if (this.pc === 7) { -//│ a = tmp23; +//│ a = tmp22; //│ scrut1 = false; //│ if (scrut1 === true) { -//│ tmp24 = A12.f(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23 = A12.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 3; -//│ return globalThis.Predef.__appendInCont(tmp24, this) +//│ return globalThis.Predef.__appendInCont(tmp23, this) //│ } //│ this.pc = 3; //│ continue contLoop; //│ } else { -//│ tmp24 = A13.f(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23 = A13.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 4; -//│ return globalThis.Predef.__appendInCont(tmp24, this) +//│ return globalThis.Predef.__appendInCont(tmp23, this) //│ } //│ this.pc = 4; //│ continue contLoop; @@ -475,11 +475,11 @@ foo(h) //│ this.pc = 7; //│ continue contLoop; //│ } else if (this.pc === 6) { -//│ b = tmp24; -//│ tmp25 = A14(); -//│ if (tmp25 instanceof globalThis.Predef.__EffectSig.class) { +//│ b = tmp23; +//│ tmp24 = A14(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 5; -//│ return globalThis.Predef.__appendInCont(tmp25, this) +//│ return globalThis.Predef.__appendInCont(tmp24, this) //│ } //│ this.pc = 5; //│ continue contLoop; @@ -490,10 +490,10 @@ foo(h) //│ this.pc = 6; //│ continue contLoop; //│ } else if (this.pc === 5) { -//│ tmp26 = tmp25 * 100; -//│ tmp27 = a * 10; -//│ tmp28 = tmp26 + tmp27; -//│ return tmp28 + b +//│ tmp25 = tmp24 * 100; +//│ tmp26 = a * 10; +//│ tmp27 = tmp25 + tmp26; +//│ return tmp27 + b //│ } //│ break; //│ } @@ -531,56 +531,56 @@ foo(h) //│ } //│ static toString() { return "A"; } //│ }; -//│ tmp22 = h.perform() ?? null; -//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp22.tail.next = Cont$53(0); -//│ tmp22.tail = tmp22.tail.next; -//│ return tmp22 +//│ tmp21 = runtime.safeCall(h.perform()); +//│ if (tmp21 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp21.tail.next = Cont$53(0); +//│ tmp21.tail = tmp21.tail.next; +//│ return tmp21 //│ } //│ scrut = true; //│ if (scrut === true) { -//│ tmp23 = A10.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp23.tail.next = Cont$53(1); -//│ tmp23.tail = tmp23.tail.next; -//│ return tmp23 +//│ tmp22 = A10.f(); +//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp22.tail.next = Cont$53(1); +//│ tmp22.tail = tmp22.tail.next; +//│ return tmp22 //│ } //│ } else { -//│ tmp23 = A11.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp23.tail.next = Cont$53(2); -//│ tmp23.tail = tmp23.tail.next; -//│ return tmp23 +//│ tmp22 = A11.f(); +//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp22.tail.next = Cont$53(2); +//│ tmp22.tail = tmp22.tail.next; +//│ return tmp22 //│ } //│ } -//│ a = tmp23; +//│ a = tmp22; //│ scrut1 = false; //│ if (scrut1 === true) { -//│ tmp24 = A12.f(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp24.tail.next = Cont$53(3); -//│ tmp24.tail = tmp24.tail.next; -//│ return tmp24 +//│ tmp23 = A12.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23.tail.next = Cont$53(3); +//│ tmp23.tail = tmp23.tail.next; +//│ return tmp23 //│ } //│ } else { -//│ tmp24 = A13.f(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp24.tail.next = Cont$53(4); -//│ tmp24.tail = tmp24.tail.next; -//│ return tmp24 +//│ tmp23 = A13.f(); +//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp23.tail.next = Cont$53(4); +//│ tmp23.tail = tmp23.tail.next; +//│ return tmp23 //│ } //│ } -//│ b = tmp24; -//│ tmp25 = A14(); -//│ if (tmp25 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp25.tail.next = Cont$53(5); -//│ tmp25.tail = tmp25.tail.next; -//│ return tmp25 +//│ b = tmp23; +//│ tmp24 = A14(); +//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp24.tail.next = Cont$53(5); +//│ tmp24.tail = tmp24.tail.next; +//│ return tmp24 //│ } -//│ tmp26 = tmp25 * 100; -//│ tmp27 = a * 10; -//│ tmp28 = tmp26 + tmp27; -//│ return tmp28 + b +//│ tmp25 = tmp24 * 100; +//│ tmp26 = a * 10; +//│ tmp27 = tmp25 + tmp26; +//│ return tmp27 + b //│ }; //│ handleBlock$20 = function handleBlock$() { //│ let h, res3, Cont$53, Handler$h$22; @@ -589,8 +589,8 @@ foo(h) //│ }; //│ Handler$h$22.class = class Handler$h$21 extends Eff1 { //│ constructor() { -//│ let tmp22; -//│ tmp22 = super(); +//│ let tmp21; +//│ tmp21 = super(); //│ } //│ perform() { //│ return globalThis.Predef.__mkEffect(h, (k) => { @@ -600,8 +600,8 @@ foo(h) //│ }; //│ Cont$54.class = class Cont$51 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp22; -//│ tmp22 = super(null, null); +//│ let tmp21; +//│ tmp21 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { @@ -623,7 +623,7 @@ foo(h) //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ res4 = k(null) ?? null; +//│ res4 = runtime.safeCall(k(runtime.Unit)); //│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { //│ res4.tail.next = Cont$54(10); //│ res4.tail = res4.tail.next; @@ -643,8 +643,8 @@ foo(h) //│ }; //│ Cont$53.class = class Cont$52 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ let tmp22; -//│ tmp22 = super(null, null); +//│ let tmp21; +//│ tmp21 = super(null, null); //│ this.pc = pc; //│ } //│ resume(value$) { @@ -676,9 +676,9 @@ foo(h) //│ } //│ return res3 //│ }; -//│ tmp21 = handleBlock$20(); -//│ if (tmp21 instanceof this.Predef.__EffectSig.class) { +//│ tmp20 = handleBlock$20(); +//│ if (tmp20 instanceof this.Predef.__EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ tmp21 +//│ tmp20 //│ = 123 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 9baad4a87..f5505e223 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -82,7 +82,7 @@ f(1, 2).get() //│ return Test$(used1, unused1) //│ }; //│ tmp1 = f1(1, 2); -//│ tmp1.get() ?? null +//│ runtime.safeCall(tmp1.get()) //│ = 1 :expect 1 @@ -143,29 +143,29 @@ fun f() = f().foo() //│ JS (unsanitized): //│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; -//│ Good$ = function Good$(z$1, y$2, x$3, f$capture$0) { +//│ Good$ = function Good$(z$1, x$2, y$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ return tmp5(z$1, x$2, y$3, f$capture$0) //│ }; -//│ Good$ctor = function Good$ctor(z$1, y$2, x$3, f$capture$0) { +//│ Good$ctor = function Good$ctor(z$1, x$2, y$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ return tmp5(z$1, x$2, y$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { -//│ return (z$11, y$21, x$31, f$capture$01) => { -//│ return new Good.class()(z$11, y$21, x$31, f$capture$01); +//│ return (z$11, x$21, y$31, f$capture$01) => { +//│ return new Good.class()(z$11, x$21, y$31, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (z$1, y$2, x$3, f$capture$0) => { +//│ return (z$1, x$2, y$3, f$capture$0) => { //│ this.z$1 = z$1; -//│ this.y$2 = y$2; -//│ this.x$3 = x$3; +//│ this.x$2 = x$2; +//│ this.y$3 = y$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } @@ -173,42 +173,42 @@ f().foo() //│ foo() { //│ let tmp5, tmp6; //│ this.z$1 = 100; -//│ tmp5 = this.x$3 + this.y$2; +//│ tmp5 = this.x$2 + this.y$3; //│ tmp6 = tmp5 + this.z$1; //│ return tmp6 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; -//│ Bad$ = function Bad$(z$1, y$2, x$3, f$capture$0) { +//│ Bad$ = function Bad$(z$1, x$2, y$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ return tmp5(z$1, x$2, y$3, f$capture$0) //│ }; -//│ Bad$ctor = function Bad$ctor(z$1, y$2, x$3, f$capture$0) { +//│ Bad$ctor = function Bad$ctor(z$1, x$2, y$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(z$1, y$2, x$3, f$capture$0) +//│ return tmp5(z$1, x$2, y$3, f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { -//│ return (z$11, y$21, x$31, f$capture$01) => { -//│ return new Bad.class()(z$11, y$21, x$31, f$capture$01); +//│ return (z$11, x$21, y$31, f$capture$01) => { +//│ return new Bad.class()(z$11, x$21, y$31, f$capture$01); //│ } //│ }; //│ Bad1.class = class Bad { //│ constructor() { -//│ return (z$1, y$2, x$3, f$capture$0) => { +//│ return (z$1, x$2, y$3, f$capture$0) => { //│ this.z$1 = z$1; -//│ this.y$2 = y$2; -//│ this.x$3 = x$3; +//│ this.x$2 = x$2; +//│ this.y$3 = y$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } //│ } //│ foo() { //│ this.f$capture$0.w0$ = 10000; -//│ return null +//│ return runtime.Unit //│ } //│ toString() { return "Bad(" + "" + ")"; } //│ }; @@ -228,12 +228,12 @@ f().foo() //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(z, y, x, capture); -//│ tmp6 = tmp5.foo() ?? null; -//│ return Good$(z, y, x, capture) +//│ tmp5 = Bad$(z, x, y, capture); +//│ tmp6 = runtime.safeCall(tmp5.foo()); +//│ return Good$(z, x, y, capture) //│ }; //│ tmp4 = f5(); -//│ tmp4.foo() ?? null +//│ runtime.safeCall(tmp4.foo()) //│ = 10111 :fixme @@ -244,4 +244,5 @@ class A(a) with fun g() = x g() A(2).f() -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'null' +//│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 9d40a496a..98ea04274 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -64,7 +64,7 @@ f(1, 2) //│ used2 = unused1; //│ unused2 = 2; //│ tmp = g$1(used2, used1, used2); -//│ tmp1 = tmp(used2) ?? null; +//│ tmp1 = runtime.safeCall(tmp(used2)); //│ return tmp1 + unused2 //│ }; //│ f1(1, 2) @@ -109,8 +109,8 @@ f(1, 2) //│ let used2, unused2, foo, tmp; //│ used2 = unused1; //│ unused2 = 2; -//│ foo = g3(used1, used2) ?? null; -//│ tmp = foo(used2) ?? null; +//│ foo = runtime.safeCall(g3(used1, used2)); +//│ tmp = runtime.safeCall(foo(used2)); //│ return tmp + unused2 //│ }; //│ f3(1, 2) @@ -151,7 +151,7 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let g5, f5, g$4; -//│ g$4 = function g$(a3, a4, a5, a1, a6, a2) { +//│ g$4 = function g$(a1, a6, a2, a5, a3, a4) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; @@ -159,14 +159,14 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6 //│ }; -//│ g5 = function g(a3, a4, a5, a1, a6, a2) { +//│ g5 = function g(a1, a6, a2, a5, a3, a4) { //│ return () => { -//│ return g$4(a3, a4, a5, a1, a6, a2) +//│ return g$4(a1, a6, a2, a5, a3, a4) //│ } //│ }; //│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$4(a3, a4, a5, a1, a6, a2); +//│ tmp = g$4(a1, a6, a2, a5, a3, a4); //│ return tmp //│ }; //│ f5(1, 2, 3, 4, 5, 6) @@ -191,7 +191,7 @@ x + y //│ let g6, h, f6, Tuple1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; //│ g$5 = function g$(f$capture2) { //│ f$capture2.a0$ = 1; -//│ return null +//│ return runtime.Unit //│ }; //│ g6 = function g(f$capture2) { //│ return () => { @@ -219,8 +219,8 @@ x + y //│ let capture, g$this, h$this; //│ capture = new f$capture1(null); //│ capture.a0$ = 0; -//│ g$this = g6(capture) ?? null; -//│ h$this = h(capture) ?? null; +//│ g$this = runtime.safeCall(g6(capture)); +//│ h$this = runtime.safeCall(h(capture)); //│ return Tuple1(g$this, h$this) //│ }; //│ Tuple1 = function Tuple(a1, b1) { @@ -237,12 +237,12 @@ x + y //│ ret = tmp; //│ f11 = ret.a; //│ f21 = ret.b; -//│ tmp1 = f21() ?? null; -//│ tmp2 = tmp1.toString() ?? null; +//│ tmp1 = runtime.safeCall(f21()); +//│ tmp2 = runtime.safeCall(tmp1.toString()); //│ x = tmp2; -//│ tmp3 = f11() ?? null; -//│ tmp4 = f21() ?? null; -//│ tmp5 = tmp4.toString() ?? null; +//│ tmp3 = runtime.safeCall(f11()); +//│ tmp4 = runtime.safeCall(f21()); +//│ tmp5 = runtime.safeCall(tmp4.toString()); //│ y = tmp5; //│ x + y //│ = "01" @@ -327,13 +327,13 @@ f(1, 2, 1000) //│ toString() { return "f$capture(" + globalThis.Predef.render(this.mutated0$) + ")"; } //│ }; //│ f8 = function f(unused, immutable, mutated) { -//│ let a1, tmp21, tmp22, tmp23, capture; +//│ let a1, tmp15, tmp16, tmp17, capture; //│ capture = new f$capture5(mutated); -//│ tmp21 = g$6(immutable, capture); -//│ a1 = tmp21; -//│ tmp22 = h$2(immutable, capture); -//│ tmp23 = a1 + tmp22; -//│ return tmp23 + unused +//│ tmp15 = g$6(immutable, capture); +//│ a1 = tmp15; +//│ tmp16 = h$2(immutable, capture); +//│ tmp17 = a1 + tmp16; +//│ return tmp17 + unused //│ }; //│ f8(1, 2, 1000) //│ = 7 @@ -368,13 +368,13 @@ f(2) //│ JS (unsanitized): //│ let g10, h3, f12, h$3, g$9; //│ g$9 = function g$(arg, n) { -//│ let scrut, tmp21; +//│ let scrut, tmp15; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg //│ } else { -//│ tmp21 = n - 1; -//│ return h$3(arg, tmp21) +//│ tmp15 = n - 1; +//│ return h$3(arg, tmp15) //│ } //│ }; //│ g10 = function g(arg) { @@ -383,13 +383,13 @@ f(2) //│ } //│ }; //│ h$3 = function h$(arg, n) { -//│ let scrut, tmp21; +//│ let scrut, tmp15; //│ scrut = n <= 0; //│ if (scrut === true) { //│ return arg //│ } else { -//│ tmp21 = n - 1; -//│ return g$9(arg, tmp21) +//│ tmp15 = n - 1; +//│ return g$9(arg, tmp15) //│ } //│ }; //│ h3 = function h(arg) { @@ -448,7 +448,7 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let h5, f16, g14, tmp21, f$1, h$4, g$capture1; +//│ let h5, f16, g14, tmp15, f$1, h$4, g$capture1; //│ h$4 = function h$(k, x1, g$capture2) { //│ k = 5; //│ x1 = 4; @@ -483,10 +483,10 @@ g()(1) //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return f16(capture) ?? null +//│ return runtime.safeCall(f16(capture)) //│ }; -//│ tmp21 = g14(); -//│ tmp21(1) ?? null +//│ tmp15 = g14(); +//│ runtime.safeCall(tmp15(1)) //│ = 1 :expect 2 @@ -551,13 +551,12 @@ fun f(x, cond) = //│ x1 = 1; //│ x1 = 2; //│ if (cond === true) { -//│ return lambda(x1) ?? null +//│ return runtime.safeCall(lambda(x1)) //│ } else { //│ x1 = 3; -//│ return lambda1(x1) ?? null +//│ return runtime.safeCall(lambda1(x1)) //│ } //│ }; -//│ null :sjs :expect 1 @@ -580,7 +579,7 @@ f() //│ }; //│ f$2 = function f$(f$capture16) { //│ f$capture16.x0$ = 1; -//│ return null +//│ return runtime.Unit //│ }; //│ f20 = function f(f$capture16) { //│ return () => { @@ -597,10 +596,10 @@ f() //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; //│ f22 = function f() { -//│ let tmp25, capture; +//│ let tmp19, capture; //│ capture = new f$capture15(null); //│ capture.x0$ = 1; -//│ tmp25 = f$2(capture); +//│ tmp19 = f$2(capture); //│ return capture.x0$ //│ }; //│ f22() From 88cb254b0202591384c4760b592b8ae684188c2e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 8 Feb 2025 17:26:09 +0800 Subject: [PATCH 052/127] finish sccsWithInfo function --- core/shared/main/scala/utils/algorithms.scala | 82 ++++++++++++++++--- 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/core/shared/main/scala/utils/algorithms.scala b/core/shared/main/scala/utils/algorithms.scala index d961325b7..09d4d97be 100644 --- a/core/shared/main/scala/utils/algorithms.scala +++ b/core/shared/main/scala/utils/algorithms.scala @@ -32,14 +32,6 @@ object algorithms { sort(toPred, Seq()) } - private case class SccNode[A]( - val node: A, - val num: Int, - var lowlink: Int = -1, - var visited: Boolean = false, - var processed: Boolean = false - ) - /** * Partitions a graph into its strongly connected components. The input type must be able to * be hashed efficiently as it will be used as a key. @@ -49,6 +41,15 @@ object algorithms { * @return A list of strongly connected components of the graph. */ def partitionScc[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { + + case class SccNode[A]( + val node: A, + val num: Int, + var lowlink: Int = -1, + var visited: Boolean = false, + var processed: Boolean = false + ) + // pre-process: assign each node an id val edgesSet = edges.toSet val nodesUniq = (edgesSet.flatMap { case (a, b) => Set(a, b) } ++ nodes.toSet).toList @@ -61,6 +62,8 @@ object algorithms { .groupBy(_._1) .map { case (a, b) => a -> b.map(_._2) } + // Tarjan's algorithm + var stack: List[SccNode[A]] = List.empty var sccs: List[List[A]] = List.empty var i = 0 @@ -98,8 +101,65 @@ object algorithms { sccs } - // TODO - def sscsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { - ??? + + /** + * Info about a graph partitioned into its strongly-connected sets. The input type must be able to + * be hashed efficiently as it will be used as a key. + * + * @param sccs The strongly connected sets. + * @param edges The edges of the strongly-connected sets. Together with `sccs`, this forms an acyclic graph. + * @param inDegs The in-degrees of the above described graph. + * @param outDegs The out-degrees of the above described graph. + */ + case class SccsInfo[A]( + sccs: Map[Int, List[A]], + edges: Map[Int, Iterable[Int]], + inDegs: Map[Int, Int], + outDegs: Map[Int, Int], + ) + + /** + * Partitions a graph into its strongly connected components and returns additional information + * about the partition. The input type must be able to be hashed efficiently as it will be used as a key. + * + * @param edges The edges of the graph. + * @param nodes Any additional nodes that are not necessarily in the edges list. + * @return The partitioned graph and info about it. + */ + def sscsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): SccsInfo[A] = { + val sccs = partitionScc(edges, nodes) + val withIdx = sccs.zipWithIndex.map(_.swap).toMap + val lookup = ( + for { + (id, scc) <- withIdx + node <- scc + } yield node -> id + ).toMap + + val notInSccEdges = edges.map { + case (a, b) => (lookup(a), lookup(b)) + }.filter { + case (a, b) => a != b + } + + val outs = notInSccEdges.groupBy { + case (a, b) => a + } + + val sccEdges = outs.map { + case (a, edges) => a -> edges.map(_._2) + }.toMap + + val inDegs = notInSccEdges.groupBy { + case (a, b) => b + }.map { + case (b, edges) => b -> edges.size + } + + val outDegs = outs.map { + case (a, edges) => a -> edges.size + } + + SccsInfo(withIdx, sccEdges, inDegs, outDegs) } } From 420174cb60567bfc17a9292866fb4c352174e38e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 00:29:13 +0800 Subject: [PATCH 053/127] Fix analysis --- core/shared/main/scala/utils/algorithms.scala | 28 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 281 +++++++++--- hkmc2/shared/src/test/mlscript/HkScratch.mls | 189 +++++++- .../src/test/mlscript/lifter/ClassInFun.mls | 433 ++++++++++++++++-- .../src/test/mlscript/lifter/FunInFun.mls | 51 ++- 5 files changed, 860 insertions(+), 122 deletions(-) diff --git a/core/shared/main/scala/utils/algorithms.scala b/core/shared/main/scala/utils/algorithms.scala index 09d4d97be..3cf7a8c86 100644 --- a/core/shared/main/scala/utils/algorithms.scala +++ b/core/shared/main/scala/utils/algorithms.scala @@ -37,14 +37,15 @@ object algorithms { * be hashed efficiently as it will be used as a key. * * @param edges The edges of the graph. - * @param nodes Any additional nodes that are not necessarily in the edges list. + * @param nodes Any additional nodes that are not necessarily in the edges list. (Overlap is fine) * @return A list of strongly connected components of the graph. */ def partitionScc[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { case class SccNode[A]( val node: A, - val num: Int, + val id: Int, + var num: Int = -1, var lowlink: Int = -1, var visited: Boolean = false, var processed: Boolean = false @@ -54,14 +55,15 @@ object algorithms { val edgesSet = edges.toSet val nodesUniq = (edgesSet.flatMap { case (a, b) => Set(a, b) } ++ nodes.toSet).toList val nodesN = nodesUniq.zipWithIndex.map { case (node, idx) => SccNode(node, idx) } - val nodeToIdx = nodesN.map(node => node.node -> node.num).toMap - val nodesIdx = nodeToIdx.map { case (node, idx) => idx -> SccNode(node, idx) } + val nodeToIdx = nodesN.map(node => node.node -> node.id).toMap + val nodesIdx = nodesN.map { case node => node.id -> node }.toMap val neighbours = edges .map { case (a, b) => (nodeToIdx(a), nodesIdx(nodeToIdx(b))) } .groupBy(_._1) .map { case (a, b) => a -> b.map(_._2) } - + .withDefault(_ => Nil) + // Tarjan's algorithm var stack: List[SccNode[A]] = List.empty @@ -69,11 +71,12 @@ object algorithms { var i = 0 def dfs(node: SccNode[A]): Unit = { + node.num = i node.lowlink = node.num node.visited = true stack = node :: stack i += 1 - for (n <- neighbours(node.num)) { + for (n <- neighbours(node.id)) { if (!n.visited) { dfs(n) node.lowlink = n.lowlink.min(node.lowlink) @@ -86,11 +89,12 @@ object algorithms { var scc: List[A] = List.empty var cur = stack.head stack = stack.tail - while (cur.num != node.num) { - scc = node.node :: scc + while (cur.id != node.id) { + scc = cur.node :: scc cur = stack.head stack = stack.tail } + scc = cur.node :: scc sccs = scc :: sccs } } @@ -123,10 +127,10 @@ object algorithms { * about the partition. The input type must be able to be hashed efficiently as it will be used as a key. * * @param edges The edges of the graph. - * @param nodes Any additional nodes that are not necessarily in the edges list. + * @param nodes Any additional nodes that are not necessarily in the edges list. (Overlap is fine) * @return The partitioned graph and info about it. */ - def sscsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): SccsInfo[A] = { + def sccsWithInfo[A](edges: Iterable[(A, A)], nodes: Iterable[A]): SccsInfo[A] = { val sccs = partitionScc(edges, nodes) val withIdx = sccs.zipWithIndex.map(_.swap).toMap val lookup = ( @@ -146,7 +150,9 @@ object algorithms { case (a, b) => a } - val sccEdges = outs.map { + val sccEdges = withIdx.map { + case (a, _) => a -> Nil // add default case + } ++ outs.map { case (a, edges) => a -> edges.map(_._2) }.toMap diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 9c45f3f8e..68fd96369 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -47,8 +47,6 @@ object Lifter: class Lifter(using State): import Lifter.* - val varAnalyzer = new UsedVarAnalyzer - /** * The context of the class lifter. * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested defns @@ -237,6 +235,7 @@ class Lifter(using State): val includedLocals = ctx.prevFnDefns.flatMap: f => val FreeVars(vars, cap) = ctx.usedLocals(f.sym) vars.filter(!cap.contains(_)) + .sortBy(_.uid) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) @@ -627,7 +626,7 @@ class Lifter(using State): // top-level def transform(b: Block) = val blk = desugarLambdas(b) - val ctx = LifterCtx.withLocals(varAnalyzer.findUsedLocals(blk)) + val ctx = LifterCtx.withLocals(UsedVarAnalyzer(blk).findUsedLocals) val ctxx = ctx.addBmsReqdInfo(createLiftInfo(blk, ctx)) val walker = new BlockTransformerShallow(SymbolSubst()): @@ -648,78 +647,186 @@ class Lifter(using State): * * Assumes the input trees have no lambdas. */ -class UsedVarAnalyzer: +class UsedVarAnalyzer(b: Block): import Lifter.FreeVars - // TODO: these two functions are orthogonal and could be combined into one, - // which reduces the number of passes over the tree by one + private case class AccessInfo( + accessed: Set[Local], + mutated: Set[Local], + refdDefns: Set[BlockMemberSymbol]): + def ++(that: AccessInfo) = AccessInfo( + accessed ++ that.accessed, + mutated ++ that.mutated, + refdDefns ++ that.refdDefns + ) + def addAccess(l: Local) = this.copy(accessed = accessed + l) + def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) + def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) + private object AccessInfo: + val empty = AccessInfo(Set.empty, Set.empty, Set.empty) - private val usedVarsCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty - private def getUsedVars(f: FunDefn): Set[Local] = usedVarsCache.get(f.sym) match - case Some(value) => value - case None => - val fVars = Lifter.getVars(f) - var usedVars: Set[Local] = Set.empty + // 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 getDefinedLocals: (Map[BlockMemberSymbol, Set[Local]], Map[BlockMemberSymbol, Defn]) = + var defnsMap: Map[BlockMemberSymbol, Defn] = Map.empty + var usedMap: Map[BlockMemberSymbol, Set[Local]] = Map.empty + + def getDefinedLocalsFn(f: FunDefn, existing: Set[Local]): Unit = + val thisVars = Lifter.getVars(f) -- existing + val newExisting = existing ++ thisVars + defnsMap += (f.sym -> f) + usedMap += (f.sym -> thisVars) val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = - usedVars ++= fVars.intersect(defn.freeVars) - super.applyDefn(defn) - + override def applyDefn(defn: Defn): Defn = + getDefinedLocalsDefn(defn, newExisting) + defn walker.applyBlock(f.body) - - usedVarsCache.addOne(f.sym -> usedVars) - fVars + + def getDefinedLocalsDefn(d: Defn, existing: Set[Local]): Unit = + d match + case f: FunDefn => + getDefinedLocalsFn(f, existing) + case c: ClsLikeDefn => + getDefinedLocalsCls(c, existing) + case d => Map.empty + + def getDefinedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + defnsMap += (c.sym -> c) + val newExisting = existing ++ c.preCtor.definedVars ++ c.ctor.definedVars + for f <- c.methods do getDefinedLocalsFn(f, newExisting) - private val blkMutCache: MutMap[Local, Set[Local]] = MutMap.empty - private def blkMutationsShallow(b: Block, cacheId: Opt[Local] = N): Set[Local] = + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = + getDefinedLocalsDefn(defn, b.definedVars) + defn + walker.applyBlock(b) + (usedMap, defnsMap) + + private val (definedLocals, defnsMap) = getDefinedLocals + + 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 mutated: Set[Local] = Set.empty + var accessed: AccessInfo = AccessInfo.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Assign(lhs, rhs, rest) => - mutated += lhs + accessed = accessed.addMutated(lhs) + applyResult(rhs) applyBlock(rest) case Label(label, body, rest) => - mutated ++= blkMutationsShallow(body, S(label)) + accessed ++= blkAccessesShallow(body, S(label)) applyBlock(rest) case _ => super.applyBlock(b) - override def applyDefn(defn: Defn): Defn = - mutated ++= findMutationsShallow(defn) - super.applyDefn(defn) + override def applyValue(v: Value): Value = v match + case Value.Ref(l: BlockMemberSymbol) => + accessed = accessed.addRefdDefn(l); v + case Value.Ref(l) => + accessed = accessed.addAccess(l); v + case _ => super.applyValue(v) walker.applyBlock(b) cacheId match case None => () - case Some(value) => blkMutCache.addOne(value -> mutated) + case Some(value) => blkMutCache.addOne(value -> accessed) - mutated + accessed - private val mutatedCache: MutMap[BlockMemberSymbol, Set[Local]] = MutMap.empty + private val accessedCache: MutMap[BlockMemberSymbol, AccessInfo] = MutMap.empty /** * Finds the variables which this definition could possibly mutate, excluding mutations through - * calls to other functions. + * calls to other functions and, in the case of functions, mutations of its own variables. * * @param defn The definition to search through. * @return The variables which this definition could possibly mutate. */ - private def findMutationsShallow(defn: Defn): Set[Local] = mutatedCache.get(defn.sym) match + private def findAccessesShallow(defn: Defn): AccessInfo = accessedCache.get(defn.sym) match case Some(value) => value case None => val ret = defn match - case f: FunDefn => - blkMutationsShallow(f.body) - case c: ClsLikeDefn => - blkMutationsShallow(c.preCtor) ++ blkMutationsShallow(c.ctor) ++ c.methods.flatMap(findMutationsShallow) - case _: ValDefn => Set.empty - mutatedCache.addOne(defn.sym -> ret) + case f: FunDefn => + val fVars = definedLocals(f.sym) + blkAccessesShallow(f.body) + case c: ClsLikeDefn => + c.methods.foldLeft(blkAccessesShallow(c.preCtor) ++ blkAccessesShallow(c.ctor)): + // here, we count the class as "accessing" all its methods (since they could be invoked anywhere) + case (acc, defn) => acc.addAccess(defn.sym) ++ findAccessesShallow(defn) + case _: ValDefn => AccessInfo.empty + accessedCache.addOne(defn.sym -> ret) ret + // MUST be called from a top-level defn + private def findAccesses(f: FunDefn): Map[BlockMemberSymbol, AccessInfo] = + var defns: List[Defn] = Nil + val walker = new BlockTransformer(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = + defn match + case f: FunDefn => defns +:= f + case c: ClsLikeDefn => defns +:= c + case _ => + super.applyDefn(defn) + walker.applyBlock(f.body) + + val defnSyms = defns.map(_.sym).toSet + + val accessInfo = defns.map: d => + val accesses = findAccessesShallow(d) + d.sym -> accesses.copy(refdDefns = accesses.refdDefns.intersect(defnSyms)) + + val accessInfoMap = accessInfo.toMap + + val edges = + for + (sym, AccessInfo(_, _, refd)) <- accessInfo + r <- refd + if defnSyms.contains(r) + yield sym -> 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) + + // 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) + + private def findAccessesTop = + var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = defn match + case f: FunDefn => + accessMap ++= findAccesses(f); f + case c: ClsLikeDefn => + for f <- c.methods do accessMap ++= findAccesses(f); c + case _ => super.applyDefn(defn) + walker.applyBlock(b) + accessMap + + private 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. @@ -730,7 +837,7 @@ class UsedVarAnalyzer: case c: ClsLikeDefn => c.sym -> c .toMap - val thisVars = Lifter.getVars(f) + val thisVars = definedLocals(f.sym) case class CaptureInfo(reqCapture: Set[Local], hasReader: Set[Local], hasMutator: Set[Local]) @@ -772,7 +879,7 @@ class UsedVarAnalyzer: // See the above TODO val c @ CaptureInfo(req, read, mut) = rec(body) merge(c) - reqCapture ++= read.intersect(blkMutationsShallow(body, S(label))) + reqCapture ++= read.intersect(blkAccessesShallow(body, S(label)).mutated) reqCapture ++= mut.intersect(body.freeVars) applyBlock(rest) b @@ -792,23 +899,70 @@ class UsedVarAnalyzer: hasMutator = Set.empty b case _ => super.applyBlock(b) + + def handleCalledBms(l: BlockMemberSymbol) = defnSyms.get(l) match + case None => () + case Some(defn) => + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val muts = muted.intersect(thisVars) + val reads = defn.freeVars.intersect(thisVars) -- muts + // 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 + reqCapture += l + hasMutator += l + for l <- reads do + if hasMutator.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 + // many mutators could be created from it + for + sym <- refd + l <- accessMap(sym).mutated + do + reqCapture += l + hasMutator += l + + override def applyResult(r: Result): Result = r match + case Call(Value.Ref(l: BlockMemberSymbol), args) => + args.map(super.applyArg(_)) + handleCalledBms(l) + r + case Instantiate(Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")), args) => + args.map(super.applyPath(_)) + handleCalledBms(l) + r + case _ => super.applyResult(r) override def applyValue(v: Value): Value = v match case Value.Ref(l: BlockMemberSymbol) => defnSyms.get(l) match case None => super.applyValue(v) - case Some(defn) => - val muts = findMutationsShallow(defn).intersect(thisVars) + case Some(defn) => + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val muts = muted.intersect(thisVars) val reads = defn.freeVars.intersect(thisVars) -- muts - // functions mutating a variable will always need a capture + // this is a naked reference, we assume things it mutates always needs a capture for l <- muts do - if hasReader.contains(l) || hasMutator.contains(l) || defn.isInstanceOf[FunDefn] then - reqCapture += l + reqCapture += l hasMutator += l for l <- reads do if hasMutator.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 + // many mutators could be created from it + for + sym <- refd + l <- accessMap(sym).mutated + do + reqCapture += l + hasMutator += l + v case Value.Ref(l) => if hasMutator.contains(l) then reqCapture += (l) @@ -825,9 +979,8 @@ class UsedVarAnalyzer: // 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 - def findUsedLocalsFn(f: FunDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = - val thisVars = Lifter.getVars(f) -- existing - val newExisting = existing ++ thisVars + private def findUsedLocalsFn(f: FunDefn): Map[BlockMemberSymbol, FreeVars] = + val thisVars = definedLocals(f.sym) val (vars, cap) = reqdCaptureLocals(f) @@ -836,21 +989,27 @@ class UsedVarAnalyzer: val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = defn match case f: FunDefn => - usedMap ++= findUsedLocalsFn(f, newExisting) + usedMap ++= findUsedLocalsFn(f) f case c: ClsLikeDefn => - val newNewExisting = newExisting ++ c.preCtor.definedVars ++ c.ctor.definedVars for f <- c.methods do - usedMap ++= findUsedLocalsFn(f, newNewExisting) + usedMap ++= findUsedLocalsFn(f) c case d => super.applyDefn(d) walker.applyBlock(f.body) usedMap - def findUsedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Map[BlockMemberSymbol, FreeVars] = - val newExisting = existing ++ c.preCtor.definedVars ++ c.ctor.definedVars + private def findUsedLocalsDefn(d: Defn) = + d match + case f: FunDefn => + findUsedLocalsFn(f) + case c: ClsLikeDefn => + findUsedLocalsCls(c) + case d => Map.empty + + private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = c.methods.foldLeft(Map.empty): - case (acc, f) => acc ++ findUsedLocalsFn(f, newExisting) + case (acc, f) => acc ++ findUsedLocalsFn(f) /** * Finds the used locals of functions which have been used by their nested definitions. @@ -858,16 +1017,12 @@ class UsedVarAnalyzer: * @param b * @return */ - def findUsedLocals(b: Block): Lifter.UsedLocalsMap = + def findUsedLocals: Lifter.UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = defn match - case f: FunDefn => - usedMap ++= findUsedLocalsFn(f, b.definedVars) - f - case c: ClsLikeDefn => - usedMap ++= findUsedLocalsCls(c, b.definedVars) - c - case d => super.applyDefn(d) + override def applyDefn(defn: Defn): Defn = + usedMap ++= findUsedLocalsDefn(defn) + defn + walker.applyBlock(b) Lifter.UsedLocalsMap(usedMap) \ No newline at end of file diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..ed4b0ae46 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,5 +7,190 @@ :global :d - - +:handler +:stackSafe 10 +:sjs +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +sum(100) +//│ Elab: { ‹› fun member:sum‹370›(Param(‹›,n‹371›,None), ) = if { let $scrut‹376› = builtin:==‹9›#0(n‹371›#666, 0); scrut is true -> { else 0 }; else builtin:+‹18›#0(n‹371›#666, member:sum‹370›#666(builtin:-‹7›#0(n‹371›#666, 1))) }; member:sum‹370›#666(100) } +//│ JS (unsanitized): +//│ let sum, res, handleBlock$; +//│ sum = function sum(n) { +//│ let scrut, tmp, tmp1, curDepth, stackDelayRes, Cont$3; +//│ Cont$3 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp2; +//│ tmp2 = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ stackDelayRes = value$; +//│ } else if (this.pc === 1) { +//│ tmp1 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0 +//│ } else { +//│ tmp = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ tmp1 = sum(tmp); +//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 1; +//│ return globalThis.Predef.__appendInCont(tmp1, this) +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 1) { +//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); +//│ return n + tmp1 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ curDepth = globalThis.Predef.__stackDepth; +//│ stackDelayRes = globalThis.Predef.checkDepth(); +//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { +//│ stackDelayRes.tail.next = Cont$3(0); +//│ stackDelayRes.tail = stackDelayRes.tail.next; +//│ return stackDelayRes +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0 +//│ } else { +//│ tmp = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ tmp1 = sum(tmp); +//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp1.tail.next = Cont$3(1); +//│ tmp1.tail = tmp1.tail.next; +//│ return tmp1 +//│ } +//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); +//│ return n + tmp1 +//│ } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let stackHandler, res1, Cont$3, StackDelay$1; +//│ StackDelay$1 = function StackDelay$() { +//│ return new StackDelay$.class(); +//│ }; +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { +//│ constructor() { +//│ let tmp; +//│ tmp = super(); +//│ } +//│ perform() { +//│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { +//│ let res2, Cont$4; +//│ Cont$4 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 5) { +//│ res2 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 5) { +//│ if (res2 instanceof globalThis.Predef.__Return.class) { +//│ return res2 +//│ } +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ return res2 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ res2 = resume(); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$4(5); +//│ res2.tail = res2.tail.next; +//│ return res2 +//│ } +//│ if (res2 instanceof globalThis.Predef.__Return.class) { +//│ return res2 +//│ } +//│ return res2 +//│ }) +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ stackHandler = StackDelay$1(); +//│ Cont$3 = function Cont$(pc1) { +//│ return new Cont$.class(pc1); +//│ }; +//│ Cont$3.class = class Cont$2 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ } +//│ resume(value$) { +//│ if (this.pc === 3) { +//│ res1 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 3) { +//│ if (res1 instanceof globalThis.Predef.__Return.class) { +//│ return res1 +//│ } +//│ this.pc = 4; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ return res1 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ globalThis.Predef.__stackLimit = 10; +//│ globalThis.Predef.__stackOffset = 0; +//│ globalThis.Predef.__stackDepth = 1; +//│ globalThis.Predef.__stackHandler = stackHandler; +//│ res1 = sum(100); +//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { +//│ res1.tail.next = Cont$3(3); +//│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler) +//│ } +//│ if (res1 instanceof globalThis.Predef.__Return.class) { +//│ return res1 +//│ } +//│ return res1 +//│ }; +//│ res = handleBlock$(); +//│ if (res instanceof this.Predef.__EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ this.Predef.__stackDepth = 0; +//│ this.Predef.__stackHandler = null; +//│ res +//│ = 5050 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index f5505e223..62ae6bec4 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -27,18 +27,18 @@ fun f(used1, unused1) = f(1, 2).get() //│ JS (unsanitized): //│ let h, Test1, g, f1, tmp1, Test$ctor, Test$, g$, h$; -//│ h$ = function h$(used3, used1) { +//│ h$ = function h$(used1, used3) { //│ return used3 //│ }; -//│ h = function h(used3, used1) { +//│ h = function h(used1, used3) { //│ return () => { -//│ return h$(used3, used1) +//│ return h$(used1, used3) //│ } //│ }; //│ g$ = function g$(used1, g_arg) { //│ let used3, tmp2; //│ used3 = 2; -//│ tmp2 = h$(used3, used1); +//│ tmp2 = h$(used1, used3); //│ return used1 + tmp2 //│ }; //│ g = function g(used1) { @@ -114,6 +114,7 @@ f(1, 2).get() //│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor //│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' +:sjs :expect 2 fun f(x) = class A() with @@ -121,6 +122,55 @@ fun f(x) = A().f() x f(1) +//│ JS (unsanitized): +//│ let A1, f4, A$ctor, A$, f$capture3; +//│ A$ = function A$(f$capture$0) { +//│ let tmp4; +//│ tmp4 = new A1(); +//│ return tmp4(f$capture$0) +//│ }; +//│ A$ctor = function A$ctor(f$capture$0) { +//│ return () => { +//│ let tmp4; +//│ tmp4 = new A1(); +//│ return tmp4(f$capture$0) +//│ } +//│ }; +//│ A1 = function A() { +//│ return (f$capture$01) => { +//│ return new A.class()(f$capture$01); +//│ } +//│ }; +//│ A1.class = class A { +//│ constructor() { +//│ return (f$capture$0) => { +//│ this.f$capture$0 = f$capture$0; +//│ return this; +//│ } +//│ } +//│ f() { +//│ this.f$capture$0.x0$ = 2; +//│ return runtime.Unit +//│ } +//│ toString() { return "A(" + "" + ")"; } +//│ }; +//│ f$capture3 = function f$capture(x0$1) { +//│ return new f$capture.class(x0$1); +//│ }; +//│ f$capture3.class = class f$capture2 { +//│ constructor(x0$) { +//│ this.x0$ = x0$; +//│ } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } +//│ }; +//│ f4 = function f(x) { +//│ let tmp4, tmp5, capture; +//│ capture = new f$capture3(x); +//│ tmp4 = A$(capture); +//│ tmp5 = runtime.safeCall(tmp4.f()); +//│ return capture.x0$ +//│ }; +//│ f4(1) //│ = 2 // only w should be in a capture @@ -143,65 +193,65 @@ fun f() = f().foo() //│ JS (unsanitized): //│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; -//│ Good$ = function Good$(z$1, x$2, y$3, f$capture$0) { +//│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(z$1, x$2, y$3, f$capture$0) +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ }; -//│ Good$ctor = function Good$ctor(z$1, x$2, y$3, f$capture$0) { +//│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(z$1, x$2, y$3, f$capture$0) +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { -//│ return (z$11, x$21, y$31, f$capture$01) => { -//│ return new Good.class()(z$11, x$21, y$31, f$capture$01); +//│ return (x$11, y$21, z$31, f$capture$01) => { +//│ return new Good.class()(x$11, y$21, z$31, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (z$1, x$2, y$3, f$capture$0) => { -//│ this.z$1 = z$1; -//│ this.x$2 = x$2; -//│ this.y$3 = y$3; +//│ return (x$1, y$2, z$3, f$capture$0) => { +//│ this.x$1 = x$1; +//│ this.y$2 = y$2; +//│ this.z$3 = z$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } //│ } //│ foo() { //│ let tmp5, tmp6; -//│ this.z$1 = 100; -//│ tmp5 = this.x$2 + this.y$3; -//│ tmp6 = tmp5 + this.z$1; +//│ this.z$3 = 100; +//│ tmp5 = this.x$1 + this.y$2; +//│ tmp6 = tmp5 + this.z$3; //│ return tmp6 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; -//│ Bad$ = function Bad$(z$1, x$2, y$3, f$capture$0) { +//│ Bad$ = function Bad$(x$1, y$2, z$3, f$capture$0) { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(z$1, x$2, y$3, f$capture$0) +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ }; -//│ Bad$ctor = function Bad$ctor(z$1, x$2, y$3, f$capture$0) { +//│ Bad$ctor = function Bad$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(z$1, x$2, y$3, f$capture$0) +//│ return tmp5(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { -//│ return (z$11, x$21, y$31, f$capture$01) => { -//│ return new Bad.class()(z$11, x$21, y$31, f$capture$01); +//│ return (x$11, y$21, z$31, f$capture$01) => { +//│ return new Bad.class()(x$11, y$21, z$31, f$capture$01); //│ } //│ }; //│ Bad1.class = class Bad { //│ constructor() { -//│ return (z$1, x$2, y$3, f$capture$0) => { -//│ this.z$1 = z$1; -//│ this.x$2 = x$2; -//│ this.y$3 = y$3; +//│ return (x$1, y$2, z$3, f$capture$0) => { +//│ this.x$1 = x$1; +//│ this.y$2 = y$2; +//│ this.z$3 = z$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } @@ -228,9 +278,9 @@ f().foo() //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(z, x, y, capture); +//│ tmp5 = Bad$(x, y, z, capture); //│ tmp6 = runtime.safeCall(tmp5.foo()); -//│ return Good$(z, x, y, capture) +//│ return Good$(x, y, z, capture) //│ }; //│ tmp4 = f5(); //│ runtime.safeCall(tmp4.foo()) @@ -246,3 +296,328 @@ class A(a) with A(2).f() //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + +:expect 2 +fun f(x) = + class A() with + fun newA() = A() + fun foo() = + set x += 1 + x + fun getX() = x + A() +let a = f(0) +a.foo() +let b = a.newA() +b.foo() +a.getX() +//│ = 2 +//│ a = A() +//│ b = A() + +// handler + +:handler +:stackSafe 10 +:sjs +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +sum(100) +//│ JS (unsanitized): +//│ let sum, res, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$1, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor, StackDelay$$, lambda$1, Cont$$ctor5, Cont$$5, sum$capture1, handleBlock$$capture3, lambda$capture3; +//│ Cont$$3 = function Cont$$(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0, pc) { +//│ let tmp10; +//│ tmp10 = new Cont$9(pc); +//│ return tmp10(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) +//│ }; +//│ Cont$$ctor3 = function Cont$$ctor(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) { +//│ return (pc) => { +//│ let tmp10; +//│ tmp10 = new Cont$9(pc); +//│ return tmp10(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) +//│ } +//│ }; +//│ Cont$9 = function Cont$(pc1) { +//│ return (n$11, scrut$21, tmp$31, curDepth$41, sum$capture$01) => { +//│ return new Cont$.class(pc1)(n$11, scrut$21, tmp$31, curDepth$41, sum$capture$01); +//│ } +//│ }; +//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) => { +//│ let tmp10; +//│ tmp10 = super(null, null); +//│ this.pc = pc; +//│ this.n$1 = n$1; +//│ this.scrut$2 = scrut$2; +//│ this.tmp$3 = tmp$3; +//│ this.curDepth$4 = curDepth$4; +//│ this.sum$capture$0 = sum$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.sum$capture$0.stackDelayRes0$ = value$; +//│ } else if (this.pc === 1) { +//│ this.sum$capture$0.tmp1$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ this.scrut$2 = this.n$1 == 0; +//│ if (this.scrut$2 === true) { +//│ return 0 +//│ } else { +//│ this.tmp$3 = this.n$1 - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ this.sum$capture$0.tmp1$ = sum(this.tmp$3); +//│ if (this.sum$capture$0.tmp1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 1; +//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp1$, this) +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 1) { +//│ this.sum$capture$0.tmp1$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp1$, this.curDepth$4); +//│ return this.n$1 + this.sum$capture$0.tmp1$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ sum$capture1 = function sum$capture(stackDelayRes0$1, tmp1$1) { +//│ return new sum$capture.class(stackDelayRes0$1, tmp1$1); +//│ }; +//│ sum$capture1.class = class sum$capture { +//│ constructor(stackDelayRes0$, tmp1$) { +//│ this.stackDelayRes0$ = stackDelayRes0$; +//│ this.tmp1$ = tmp1$; +//│ } +//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.tmp1$) + ")"; } +//│ }; +//│ sum = function sum(n) { +//│ let scrut, tmp10, curDepth, capture; +//│ capture = new sum$capture1(null, null); +//│ curDepth = globalThis.Predef.__stackDepth; +//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); +//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.stackDelayRes0$.tail.next = Cont$$3(n, scrut, tmp10, curDepth, capture, 0); +//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; +//│ return capture.stackDelayRes0$ +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { +//│ return 0 +//│ } else { +//│ tmp10 = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ capture.tmp1$ = sum(tmp10); +//│ if (capture.tmp1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.tmp1$.tail.next = Cont$$3(n, scrut, tmp10, curDepth, capture, 1); +//│ capture.tmp1$.tail = capture.tmp1$.tail.next; +//│ return capture.tmp1$ +//│ } +//│ capture.tmp1$ = globalThis.Predef.resetDepth(capture.tmp1$, curDepth); +//│ return n + capture.tmp1$ +//│ } +//│ }; +//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { +//│ let tmp10; +//│ tmp10 = new StackDelay$1(); +//│ return tmp10(handleBlock$$capture$0) +//│ }; +//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { +//│ return () => { +//│ let tmp10; +//│ tmp10 = new StackDelay$1(); +//│ return tmp10(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$$5 = function Cont$$(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1, pc) { +//│ let tmp10; +//│ tmp10 = new Cont$11(pc); +//│ return tmp10(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) +//│ }; +//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) { +//│ return (pc) => { +//│ let tmp10; +//│ tmp10 = new Cont$11(pc); +//│ return tmp10(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) +//│ } +//│ }; +//│ Cont$11 = function Cont$(pc1) { +//│ return (StackDelay$$instance$21, lambda$capture$01, handleBlock$$capture$11) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$21, lambda$capture$01, handleBlock$$capture$11); +//│ } +//│ }; +//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) => { +//│ let tmp10; +//│ tmp10 = super(null, null); +//│ this.pc = pc; +//│ this.StackDelay$$instance$2 = StackDelay$$instance$2; +//│ this.lambda$capture$0 = lambda$capture$0; +//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 5) { +//│ this.lambda$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 5) { +//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ lambda$capture3 = function lambda$capture(res0$1) { +//│ return new lambda$capture.class(res0$1); +//│ }; +//│ lambda$capture3.class = class lambda$capture2 { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ lambda$1 = function lambda$(StackDelay$$instance, handleBlock$$capture4, resume) { +//│ let capture; +//│ capture = new lambda$capture3(null); +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ capture.res0$ = resume(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, capture, handleBlock$$capture4, 5); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$ +//│ } +//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res0$ +//│ } +//│ return capture.res0$ +//│ }; +//│ lambda1 = function lambda(StackDelay$$instance, handleBlock$$capture4) { +//│ return (resume) => { +//│ return lambda$1(StackDelay$$instance, handleBlock$$capture4, resume) +//│ } +//│ }; +//│ StackDelay$1 = function StackDelay$() { +//│ return (handleBlock$$capture$01) => { +//│ return new StackDelay$.class()(handleBlock$$capture$01); +//│ } +//│ }; +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { +//│ constructor() { +//│ return (handleBlock$$capture$0) => { +//│ let tmp10; +//│ tmp10 = super(); +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda1(this, this.handleBlock$$capture$0)); +//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { +//│ let tmp10; +//│ tmp10 = new Cont$10(pc); +//│ return tmp10(handleBlock$$capture$0) +//│ }; +//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { +//│ return (pc) => { +//│ let tmp10; +//│ tmp10 = new Cont$10(pc); +//│ return tmp10(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$10 = function Cont$(pc1) { +//│ return (handleBlock$$capture$01) => { +//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ } +//│ }; +//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (handleBlock$$capture$0) => { +//│ let tmp10; +//│ tmp10 = super(null, null); +//│ this.pc = pc; +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 3) { +//│ this.handleBlock$$capture$0.res1$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 3) { +//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ this.pc = 4; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1, res1$1) { +//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ }; +//│ handleBlock$$capture3.class = class handleBlock$$capture2 { +//│ constructor(stackHandler0$, res1$) { +//│ this.stackHandler0$ = stackHandler0$; +//│ this.res1$ = res1$; +//│ } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ }; +//│ handleBlock$1 = function handleBlock$() { +//│ let capture; +//│ capture = new handleBlock$$capture3(null, null); +//│ capture.stackHandler0$ = StackDelay$$(capture); +//│ globalThis.Predef.__stackLimit = 10; +//│ globalThis.Predef.__stackOffset = 0; +//│ globalThis.Predef.__stackDepth = 1; +//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; +//│ capture.res1$ = sum(100); +//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res1$.tail.next = Cont$$4(capture, 3); +//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ } +//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res1$ +//│ } +//│ return capture.res1$ +//│ }; +//│ res = handleBlock$1(); +//│ if (res instanceof this.Predef.__EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ this.Predef.__stackDepth = 0; +//│ this.Predef.__stackHandler = null; +//│ res +//│ = 5050 diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 98ea04274..9f9754169 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -45,7 +45,7 @@ fun f(used1, unused1) = f(1, 2) //│ JS (unsanitized): //│ let g1, f1, g$1; -//│ g$1 = function g$(used2, used1, g_arg) { +//│ g$1 = function g$(used1, used2, g_arg) { //│ return (g_arg2) => { //│ let used3, tmp, tmp1; //│ used3 = 2; @@ -54,16 +54,16 @@ f(1, 2) //│ return tmp1 + g_arg2 //│ } //│ }; -//│ g1 = function g(used2, used1) { +//│ g1 = function g(used1, used2) { //│ return (g_arg) => { -//│ return g$1(used2, used1, g_arg) +//│ return g$1(used1, used2, g_arg) //│ } //│ }; //│ f1 = function f(used1, unused1) { //│ let used2, unused2, tmp, tmp1; //│ used2 = unused1; //│ unused2 = 2; -//│ tmp = g$1(used2, used1, used2); +//│ tmp = g$1(used1, used2, used2); //│ tmp1 = runtime.safeCall(tmp(used2)); //│ return tmp1 + unused2 //│ }; @@ -140,9 +140,6 @@ f(1, 2) //│ f4(1, 2) //│ = 5 - - //│ (used11) => - // preserve order :sjs fun f(a1, a2, a3, a4, a5, a6) = @@ -151,7 +148,7 @@ fun f(a1, a2, a3, a4, a5, a6) = f(1,2,3,4,5,6) //│ JS (unsanitized): //│ let g5, f5, g$4; -//│ g$4 = function g$(a1, a6, a2, a5, a3, a4) { +//│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; //│ tmp1 = tmp + a3; @@ -159,14 +156,14 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6 //│ }; -//│ g5 = function g(a1, a6, a2, a5, a3, a4) { +//│ g5 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { -//│ return g$4(a1, a6, a2, a5, a3, a4) +//│ return g$4(a1, a2, a3, a4, a5, a6) //│ } //│ }; //│ f5 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; -//│ tmp = g$4(a1, a6, a2, a5, a3, a4); +//│ tmp = g$4(a1, a2, a3, a4, a5, a6); //│ return tmp //│ }; //│ f5(1, 2, 3, 4, 5, 6) @@ -264,6 +261,7 @@ fun f(used1) = fun g22() = used1 Tuple(g1(10), g22) +:sjs :expect "11110110" let ret = f(1) let gRet = ret.a @@ -276,6 +274,27 @@ hFun() let c = g2() let d = iFun() a.toString() + b + c + d +//│ JS (unsanitized): +//│ let ret1, gRet, g21, hFun, iFun, a, b, c, d, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; +//│ tmp6 = f7(1); +//│ ret1 = tmp6; +//│ gRet = ret1.a; +//│ g21 = ret1.b; +//│ hFun = gRet.a; +//│ iFun = gRet.b; +//│ tmp7 = runtime.safeCall(iFun()); +//│ a = tmp7; +//│ tmp8 = runtime.safeCall(g21()); +//│ b = tmp8; +//│ tmp9 = runtime.safeCall(hFun()); +//│ tmp10 = runtime.safeCall(g21()); +//│ c = tmp10; +//│ tmp11 = runtime.safeCall(iFun()); +//│ d = tmp11; +//│ tmp12 = runtime.safeCall(a.toString()); +//│ tmp13 = tmp12 + b; +//│ tmp14 = tmp13 + c; +//│ tmp14 + d //│ = "11110110" //│ a = 11 //│ b = 1 @@ -449,14 +468,14 @@ fun g() = g()(1) //│ JS (unsanitized): //│ let h5, f16, g14, tmp15, f$1, h$4, g$capture1; -//│ h$4 = function h$(k, x1, g$capture2) { +//│ h$4 = function h$(x1, k, g$capture2) { //│ k = 5; //│ x1 = 4; //│ return x1 + g$capture2.y0$ //│ }; -//│ h5 = function h(k, x1, g$capture2) { +//│ h5 = function h(x1, k, g$capture2) { //│ return () => { -//│ return h$4(k, x1, g$capture2) +//│ return h$4(x1, k, g$capture2) //│ } //│ }; //│ f$1 = function f$(g$capture2, x1) { @@ -503,7 +522,6 @@ ret() //│ ret = [function] :expect 6 -:fixme :lift fun f(x, y, z) = fun g() = @@ -517,8 +535,7 @@ fun f(x, y, z) = set z = 3 g f(0, 0, 0)() -//│ ═══[RUNTIME ERROR] Expected: '6', got: '1' -//│ = 1 +//│ = 6 :sjs fun f(x, cond) = From 0d6059faa7ca373fc365e3675f48423af10e74a1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 00:38:06 +0800 Subject: [PATCH 054/127] Fix instantiate --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 7 +++ .../src/test/mlscript/lifter/ClassInFun.mls | 44 +++++++++---------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 68fd96369..551f9e8ec 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -298,6 +298,13 @@ class Lifter(using State): val newArgs = args.map(applyArg(_)) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) case None => super.applyResult(r) + case c @ Instantiate(Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")), args) => + ctx.bmsReqdInfo.get(l) match + case Some(info) => + val extraArgs = getCallArgs(l, ctx) + val newArgs = args.map(applyPath(_)).map(_.asArg) + Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) + case None => super.applyResult(r) // if possible, directly create the bms and replace the result with it case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) case _ => super.applyResult(r) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 62ae6bec4..c6559e343 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -99,7 +99,6 @@ fun f(used1, unused1) = f(1, 2).get() //│ = 1 -:fixme :expect 1 fun f(used1, unused1) = fun g(g_arg) = @@ -111,8 +110,7 @@ fun f(used1, unused1) = fun get() = used1 new Test(unused1) f(1, 2).get() -//│ ═══[RUNTIME ERROR] TypeError: Test$this.class is not a constructor -//│ ═══[RUNTIME ERROR] Expected: '1', got: 'undefined' +//│ = 1 :sjs :expect 2 @@ -535,7 +533,7 @@ sum(100) //│ perform() { //│ let lambda$this; //│ lambda$this = runtime.safeCall(lambda1(this, this.handleBlock$$capture$0)); -//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler1$, lambda$this) //│ } //│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; @@ -568,50 +566,50 @@ sum(100) //│ } //│ resume(value$) { //│ if (this.pc === 3) { -//│ this.handleBlock$$capture$0.res1$ = value$; +//│ this.handleBlock$$capture$0.res0$ = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return this.handleBlock$$capture$0.res1$ +//│ if (this.handleBlock$$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return this.handleBlock$$capture$0.res0$ //│ } //│ this.pc = 4; //│ continue contLoop; //│ } else if (this.pc === 4) { -//│ return this.handleBlock$$capture$0.res1$ +//│ return this.handleBlock$$capture$0.res0$ //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1, res1$1) { -//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ handleBlock$$capture3 = function handleBlock$$capture(res0$1, stackHandler1$1) { +//│ return new handleBlock$$capture.class(res0$1, stackHandler1$1); //│ }; //│ handleBlock$$capture3.class = class handleBlock$$capture2 { -//│ constructor(stackHandler0$, res1$) { -//│ this.stackHandler0$ = stackHandler0$; -//│ this.res1$ = res1$; +//│ constructor(res0$, stackHandler1$) { +//│ this.res0$ = res0$; +//│ this.stackHandler1$ = stackHandler1$; //│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ", " + globalThis.Predef.render(this.stackHandler1$) + ")"; } //│ }; //│ handleBlock$1 = function handleBlock$() { //│ let capture; //│ capture = new handleBlock$$capture3(null, null); -//│ capture.stackHandler0$ = StackDelay$$(capture); +//│ capture.stackHandler1$ = StackDelay$$(capture); //│ globalThis.Predef.__stackLimit = 10; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; -//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; -//│ capture.res1$ = sum(100); -//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res1$.tail.next = Cont$$4(capture, 3); -//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ globalThis.Predef.__stackHandler = capture.stackHandler1$; +//│ capture.res0$ = sum(100); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$4(capture, 3); +//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, capture.stackHandler1$) //│ } -//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res1$ +//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res0$ //│ } -//│ return capture.res1$ +//│ return capture.res0$ //│ }; //│ res = handleBlock$1(); //│ if (res instanceof this.Predef.__EffectSig.class) { From 306f1b15708439b73191646bdf701f751d9f178b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 01:25:32 +0800 Subject: [PATCH 055/127] add instance check test --- .../src/test/mlscript/lifter/ClassInFun.mls | 312 +----------------- 1 file changed, 15 insertions(+), 297 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index c6559e343..d6cb6f391 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -315,307 +315,25 @@ a.getX() // handler -:handler :stackSafe 10 -:sjs +:handler fun sum(n) = if n == 0 then 0 else n + sum(n - 1) sum(100) -//│ JS (unsanitized): -//│ let sum, res, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$1, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor, StackDelay$$, lambda$1, Cont$$ctor5, Cont$$5, sum$capture1, handleBlock$$capture3, lambda$capture3; -//│ Cont$$3 = function Cont$$(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0, pc) { -//│ let tmp10; -//│ tmp10 = new Cont$9(pc); -//│ return tmp10(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) -//│ }; -//│ Cont$$ctor3 = function Cont$$ctor(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) { -//│ return (pc) => { -//│ let tmp10; -//│ tmp10 = new Cont$9(pc); -//│ return tmp10(n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) -//│ } -//│ }; -//│ Cont$9 = function Cont$(pc1) { -//│ return (n$11, scrut$21, tmp$31, curDepth$41, sum$capture$01) => { -//│ return new Cont$.class(pc1)(n$11, scrut$21, tmp$31, curDepth$41, sum$capture$01); -//│ } -//│ }; -//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (n$1, scrut$2, tmp$3, curDepth$4, sum$capture$0) => { -//│ let tmp10; -//│ tmp10 = super(null, null); -//│ this.pc = pc; -//│ this.n$1 = n$1; -//│ this.scrut$2 = scrut$2; -//│ this.tmp$3 = tmp$3; -//│ this.curDepth$4 = curDepth$4; -//│ this.sum$capture$0 = sum$capture$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ this.sum$capture$0.stackDelayRes0$ = value$; -//│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp1$ = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ this.scrut$2 = this.n$1 == 0; -//│ if (this.scrut$2 === true) { -//│ return 0 -//│ } else { -//│ this.tmp$3 = this.n$1 - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ this.sum$capture$0.tmp1$ = sum(this.tmp$3); -//│ if (this.sum$capture$0.tmp1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp1$, this) -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ break contLoop; -//│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp1$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp1$, this.curDepth$4); -//│ return this.n$1 + this.sum$capture$0.tmp1$ -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ sum$capture1 = function sum$capture(stackDelayRes0$1, tmp1$1) { -//│ return new sum$capture.class(stackDelayRes0$1, tmp1$1); -//│ }; -//│ sum$capture1.class = class sum$capture { -//│ constructor(stackDelayRes0$, tmp1$) { -//│ this.stackDelayRes0$ = stackDelayRes0$; -//│ this.tmp1$ = tmp1$; -//│ } -//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.tmp1$) + ")"; } -//│ }; -//│ sum = function sum(n) { -//│ let scrut, tmp10, curDepth, capture; -//│ capture = new sum$capture1(null, null); -//│ curDepth = globalThis.Predef.__stackDepth; -//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); -//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.stackDelayRes0$.tail.next = Cont$$3(n, scrut, tmp10, curDepth, capture, 0); -//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; -//│ return capture.stackDelayRes0$ -//│ } -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0 -//│ } else { -//│ tmp10 = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ capture.tmp1$ = sum(tmp10); -//│ if (capture.tmp1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.tmp1$.tail.next = Cont$$3(n, scrut, tmp10, curDepth, capture, 1); -//│ capture.tmp1$.tail = capture.tmp1$.tail.next; -//│ return capture.tmp1$ -//│ } -//│ capture.tmp1$ = globalThis.Predef.resetDepth(capture.tmp1$, curDepth); -//│ return n + capture.tmp1$ -//│ } -//│ }; -//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { -//│ let tmp10; -//│ tmp10 = new StackDelay$1(); -//│ return tmp10(handleBlock$$capture$0) -//│ }; -//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { -//│ return () => { -//│ let tmp10; -//│ tmp10 = new StackDelay$1(); -//│ return tmp10(handleBlock$$capture$0) -//│ } -//│ }; -//│ Cont$$5 = function Cont$$(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1, pc) { -//│ let tmp10; -//│ tmp10 = new Cont$11(pc); -//│ return tmp10(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) -//│ }; -//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) { -//│ return (pc) => { -//│ let tmp10; -//│ tmp10 = new Cont$11(pc); -//│ return tmp10(StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) -//│ } -//│ }; -//│ Cont$11 = function Cont$(pc1) { -//│ return (StackDelay$$instance$21, lambda$capture$01, handleBlock$$capture$11) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$21, lambda$capture$01, handleBlock$$capture$11); -//│ } -//│ }; -//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (StackDelay$$instance$2, lambda$capture$0, handleBlock$$capture$1) => { -//│ let tmp10; -//│ tmp10 = super(null, null); -//│ this.pc = pc; -//│ this.StackDelay$$instance$2 = StackDelay$$instance$2; -//│ this.lambda$capture$0 = lambda$capture$0; -//│ this.handleBlock$$capture$1 = handleBlock$$capture$1; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 5) { -//│ this.lambda$capture$0.res0$ = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 5) { -//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return this.lambda$capture$0.res0$ -//│ } -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ return this.lambda$capture$0.res0$ -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ lambda$capture3 = function lambda$capture(res0$1) { -//│ return new lambda$capture.class(res0$1); -//│ }; -//│ lambda$capture3.class = class lambda$capture2 { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; -//│ lambda$1 = function lambda$(StackDelay$$instance, handleBlock$$capture4, resume) { -//│ let capture; -//│ capture = new lambda$capture3(null); -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ capture.res0$ = resume(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, capture, handleBlock$$capture4, 5); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$ -//│ } -//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res0$ -//│ } -//│ return capture.res0$ -//│ }; -//│ lambda1 = function lambda(StackDelay$$instance, handleBlock$$capture4) { -//│ return (resume) => { -//│ return lambda$1(StackDelay$$instance, handleBlock$$capture4, resume) -//│ } -//│ }; -//│ StackDelay$1 = function StackDelay$() { -//│ return (handleBlock$$capture$01) => { -//│ return new StackDelay$.class()(handleBlock$$capture$01); -//│ } -//│ }; -//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { -//│ constructor() { -//│ return (handleBlock$$capture$0) => { -//│ let tmp10; -//│ tmp10 = super(); -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } -//│ } -//│ perform() { -//│ let lambda$this; -//│ lambda$this = runtime.safeCall(lambda1(this, this.handleBlock$$capture$0)); -//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler1$, lambda$this) -//│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { -//│ let tmp10; -//│ tmp10 = new Cont$10(pc); -//│ return tmp10(handleBlock$$capture$0) -//│ }; -//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { -//│ return (pc) => { -//│ let tmp10; -//│ tmp10 = new Cont$10(pc); -//│ return tmp10(handleBlock$$capture$0) -//│ } -//│ }; -//│ Cont$10 = function Cont$(pc1) { -//│ return (handleBlock$$capture$01) => { -//│ return new Cont$.class(pc1)(handleBlock$$capture$01); -//│ } -//│ }; -//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (handleBlock$$capture$0) => { -//│ let tmp10; -//│ tmp10 = super(null, null); -//│ this.pc = pc; -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 3) { -//│ this.handleBlock$$capture$0.res0$ = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 3) { -//│ if (this.handleBlock$$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return this.handleBlock$$capture$0.res0$ -//│ } -//│ this.pc = 4; -//│ continue contLoop; -//│ } else if (this.pc === 4) { -//│ return this.handleBlock$$capture$0.res0$ -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$$capture3 = function handleBlock$$capture(res0$1, stackHandler1$1) { -//│ return new handleBlock$$capture.class(res0$1, stackHandler1$1); -//│ }; -//│ handleBlock$$capture3.class = class handleBlock$$capture2 { -//│ constructor(res0$, stackHandler1$) { -//│ this.res0$ = res0$; -//│ this.stackHandler1$ = stackHandler1$; -//│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.res0$) + ", " + globalThis.Predef.render(this.stackHandler1$) + ")"; } -//│ }; -//│ handleBlock$1 = function handleBlock$() { -//│ let capture; -//│ capture = new handleBlock$$capture3(null, null); -//│ capture.stackHandler1$ = StackDelay$$(capture); -//│ globalThis.Predef.__stackLimit = 10; -//│ globalThis.Predef.__stackOffset = 0; -//│ globalThis.Predef.__stackDepth = 1; -//│ globalThis.Predef.__stackHandler = capture.stackHandler1$; -//│ capture.res0$ = sum(100); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$4(capture, 3); -//│ return globalThis.Predef.__handleBlockImpl(capture.res0$, capture.stackHandler1$) -//│ } -//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res0$ -//│ } -//│ return capture.res0$ -//│ }; -//│ res = handleBlock$1(); -//│ if (res instanceof this.Predef.__EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ this.Predef.__stackDepth = 0; -//│ this.Predef.__stackHandler = null; -//│ res //│ = 5050 + +// instance checks + +:lift +fun foo(x, n) = + class C() + let res = if x is C then "Y" else "N" + if n > 0 then res + foo(C(), n - 1) else "" + +:todo +:expect "NN" +foo(0, 2) +//│ ═══[RUNTIME ERROR] Expected: '"NN"', got: '"NY"' +//│ = "NY" From 0a6379d0f8b87a4be1d69b9ee7f7bef21d510c7e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 01:36:04 +0800 Subject: [PATCH 056/127] merge hkmc2 --- .../shared/src/main/scala/hkmc2/Config.scala | 4 + .../main/scala/hkmc2/codegen/Lowering.scala | 3 +- hkmc2/shared/src/test/mlscript/HkScratch.mls | 189 +----------------- .../src/test/mlscript/lifter/ClassInFun.mls | 4 +- .../src/test/scala/hkmc2/MLsDiffMaker.scala | 2 + 5 files changed, 12 insertions(+), 190 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index f17daeb7f..f9ef7b258 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -11,6 +11,7 @@ def config(using Config): Config = summon case class Config( sanityChecks: Opt[SanityChecks], effectHandlers: Opt[EffectHandlers], + liftDefns: Opt[LiftDefns] ): def stackSafety: Opt[StackSafety] = effectHandlers.flatMap(_.stackSafety) @@ -24,6 +25,7 @@ object Config: sanityChecks = N, // TODO make the default S // sanityChecks = S(SanityChecks(light = true)), effectHandlers = N, + liftDefns = N ) case class SanityChecks(light: Bool) @@ -35,6 +37,8 @@ object Config: val default: StackSafety = StackSafety( stackLimit = 500, ) + + case class LiftDefns() // there may be other settings in the future, having it as a case class now end Config diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index f839e34ab..a9d18d6d2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -55,7 +55,8 @@ import Subst.subst class Lowering()(using Config, TL, Raise, State, Ctx): val lowerHandlers: Bool = config.effectHandlers.isDefined - + val lift: Bool = config.liftDefns.isDefined + private lazy val unreachableFn = Select(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Predef"))(N), Tree.Ident("unreachable"))(N) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index ed4b0ae46..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,190 +7,5 @@ :global :d -:handler -:stackSafe 10 -:sjs -fun sum(n) = - if n == 0 then 0 - else - n + sum(n - 1) -sum(100) -//│ Elab: { ‹› fun member:sum‹370›(Param(‹›,n‹371›,None), ) = if { let $scrut‹376› = builtin:==‹9›#0(n‹371›#666, 0); scrut is true -> { else 0 }; else builtin:+‹18›#0(n‹371›#666, member:sum‹370›#666(builtin:-‹7›#0(n‹371›#666, 1))) }; member:sum‹370›#666(100) } -//│ JS (unsanitized): -//│ let sum, res, handleBlock$; -//│ sum = function sum(n) { -//│ let scrut, tmp, tmp1, curDepth, stackDelayRes, Cont$3; -//│ Cont$3 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp2; -//│ tmp2 = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ stackDelayRes = value$; -//│ } else if (this.pc === 1) { -//│ tmp1 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0 -//│ } else { -//│ tmp = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ tmp1 = sum(tmp); -//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { -//│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(tmp1, this) -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ break contLoop; -//│ } else if (this.pc === 1) { -//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); -//│ return n + tmp1 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ curDepth = globalThis.Predef.__stackDepth; -//│ stackDelayRes = globalThis.Predef.checkDepth(); -//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { -//│ stackDelayRes.tail.next = Cont$3(0); -//│ stackDelayRes.tail = stackDelayRes.tail.next; -//│ return stackDelayRes -//│ } -//│ scrut = n == 0; -//│ if (scrut === true) { -//│ return 0 -//│ } else { -//│ tmp = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ tmp1 = sum(tmp); -//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp1.tail.next = Cont$3(1); -//│ tmp1.tail = tmp1.tail.next; -//│ return tmp1 -//│ } -//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); -//│ return n + tmp1 -//│ } -//│ }; -//│ handleBlock$ = function handleBlock$() { -//│ let stackHandler, res1, Cont$3, StackDelay$1; -//│ StackDelay$1 = function StackDelay$() { -//│ return new StackDelay$.class(); -//│ }; -//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { -//│ constructor() { -//│ let tmp; -//│ tmp = super(); -//│ } -//│ perform() { -//│ return globalThis.Predef.__mkEffect(stackHandler, (resume) => { -//│ let res2, Cont$4; -//│ Cont$4 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$4.class = class Cont$1 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 5) { -//│ res2 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 5) { -//│ if (res2 instanceof globalThis.Predef.__Return.class) { -//│ return res2 -//│ } -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ return res2 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ res2 = resume(); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$4(5); -//│ res2.tail = res2.tail.next; -//│ return res2 -//│ } -//│ if (res2 instanceof globalThis.Predef.__Return.class) { -//│ return res2 -//│ } -//│ return res2 -//│ }) -//│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ stackHandler = StackDelay$1(); -//│ Cont$3 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$3.class = class Cont$2 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 3) { -//│ res1 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 3) { -//│ if (res1 instanceof globalThis.Predef.__Return.class) { -//│ return res1 -//│ } -//│ this.pc = 4; -//│ continue contLoop; -//│ } else if (this.pc === 4) { -//│ return res1 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ globalThis.Predef.__stackLimit = 10; -//│ globalThis.Predef.__stackOffset = 0; -//│ globalThis.Predef.__stackDepth = 1; -//│ globalThis.Predef.__stackHandler = stackHandler; -//│ res1 = sum(100); -//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$3(3); -//│ return globalThis.Predef.__handleBlockImpl(res1, stackHandler) -//│ } -//│ if (res1 instanceof globalThis.Predef.__Return.class) { -//│ return res1 -//│ } -//│ return res1 -//│ }; -//│ res = handleBlock$(); -//│ if (res instanceof this.Predef.__EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ this.Predef.__stackDepth = 0; -//│ this.Predef.__stackHandler = null; -//│ res -//│ = 5050 + + diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index d6cb6f391..45fbad23f 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -2,7 +2,7 @@ :js // make sure class fields are discriminated properly for immutable captures when supported -:handler +:effectHandlers abstract class Effect with fun perform() handle h = Effect with @@ -316,7 +316,7 @@ a.getX() // handler :stackSafe 10 -:handler +:effectHandlers fun sum(n) = if n == 0 then 0 else diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala index 83f9ed215..142730a7b 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala @@ -58,6 +58,7 @@ abstract class MLsDiffMaker extends DiffMaker: val noSanityCheck = NullaryCommand("noSanityCheck") val effectHandlers = NullaryCommand("effectHandlers") val stackSafe = Command("stackSafe")(_.trim) + val liftDefns = NullaryCommand("lift") def mkConfig: Config = import Config.* @@ -79,6 +80,7 @@ abstract class MLsDiffMaker extends DiffMaker: S(StackSafety(stackLimit = value)) , )), + liftDefns = Opt.when(liftDefns.isSet)(LiftDefns()) ) From 24b690b7a8636726b999c0d012f7510a3f7ff108 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 01:37:06 +0800 Subject: [PATCH 057/127] remove useless import --- core/shared/main/scala/utils/algorithms.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/shared/main/scala/utils/algorithms.scala b/core/shared/main/scala/utils/algorithms.scala index 3cf7a8c86..35da70909 100644 --- a/core/shared/main/scala/utils/algorithms.scala +++ b/core/shared/main/scala/utils/algorithms.scala @@ -2,7 +2,6 @@ package mlscript.utils import scala.annotation.tailrec import scala.collection.immutable.SortedMap -import scala.collection.mutable.ArrayBuffer object algorithms { From 046fd7b7a193484cba00561dd35341ab34eb910a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 01:58:20 +0800 Subject: [PATCH 058/127] refactor floatoutdefns --- .../shared/src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../main/scala/hkmc2/codegen/HandlerLowering.scala | 2 +- .../shared/src/main/scala/hkmc2/codegen/Lifter.scala | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index e4a2fd8b7..392329c0f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -146,7 +146,7 @@ sealed abstract class Block extends Product with AutoLocated: // Note that this returns the definitions in reverse order, with the bottommost definiton appearing // last. This is so that using defns.foldLeft later to add the definitions to the front of a block, // we don't need to reverse the list again to preserve the order of the definitions. - def floatOutDefns = + def floatOutDefns(ignore: Set[Local] = Set.empty) = var defns: List[Defn] = Nil val transformer = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index a5e0ebca2..6369879ac 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -352,7 +352,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def thirdPass(b: Block): Block = // to ensure the fun and class references in the continuation class are properly scoped, // we move all function defns to the top level of the handler block - val (blk, defns) = b.floatOutDefns + val (blk, defns) = b.floatOutDefns() defns.foldLeft(blk)((acc, defn) => Define(defn, acc)) private def translateFun(f: FunDefn): FunDefn = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 551f9e8ec..79b371d7d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -256,11 +256,11 @@ class Lifter(using State): case _ => Map.empty def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val (_, defns) = f.body.floatOutDefns + val (_, defns) = f.body.floatOutDefns() defns.flatMap(createLiftInfoCont(_, N, ctx.addFnDefn(f))).toMap def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val defns = c.preCtor.floatOutDefns._2 ++ c.ctor.floatOutDefns._2 + val defns = c.preCtor.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2 val newCtx = ctx.addClsDefn(c) defns.flatMap(f => createLiftInfoCont(f, N, newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, S(c), newCtx)) @@ -535,8 +535,8 @@ class Lifter(using State): case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = - val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns - val (ctor, ctorDefns) = c.ctor.floatOutDefns + val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns() + val (ctor, ctorDefns) = c.ctor.floatOutDefns() val newCtx = ctx.addIsymPath(c.isym, c.isym) // TODO: add block member symbol replacement @@ -570,7 +570,7 @@ class Lifter(using State): def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) - val (blk, nested) = f.body.floatOutDefns + val (blk, nested) = f.body.floatOutDefns() // add the mapping from this function's locals to the capture's symbols and the capture path val captureSym = FlowSymbol("capture") @@ -838,7 +838,7 @@ class UsedVarAnalyzer(b: Block): // 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.floatOutDefns + var (_, defns) = f.body.floatOutDefns() val defnSyms = defns.collect: case f: FunDefn => f.sym -> f case c: ClsLikeDefn => c.sym -> c From dab24e472a7dffc3fff70bd09315aef3fa258ac6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 02:15:49 +0800 Subject: [PATCH 059/127] refactor floatoutdefns and update ctx --- .../src/main/scala/hkmc2/codegen/Block.scala | 9 ++- .../src/main/scala/hkmc2/codegen/Lifter.scala | 63 ++++++++++++------- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 392329c0f..a2a24f55b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -146,15 +146,18 @@ sealed abstract class Block extends Product with AutoLocated: // Note that this returns the definitions in reverse order, with the bottommost definiton appearing // last. This is so that using defns.foldLeft later to add the definitions to the front of a block, // we don't need to reverse the list again to preserve the order of the definitions. - def floatOutDefns(ignore: Set[Local] = Set.empty) = + def floatOutDefns( + ignore: Defn => Bool = _ => false, + preserve: Defn => Bool = _ => false) = var defns: List[Defn] = Nil val transformer = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match - case Define(defn, rest) => defn match + case Define(defn, rest) if !ignore(defn) => defn match case v: ValDefn => super.applyBlock(b) case _ => defns ::= defn - applyBlock(rest) + if preserve(defn) then super.applyBlock(b) + else applyBlock(rest) case _ => super.applyBlock(b) (transformer.applyBlock(this), defns) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 79b371d7d..f7aa5591d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -60,7 +60,8 @@ class Lifter(using State): * @param iSymPaths The path to access a particular `innerSymbol` (possibly belonging to a previous class) in the current scope */ case class LifterCtx( - val usedLocals: UsedLocalsMap, + val usedLocals: UsedLocalsMap, + val ignoredDefns: Set[BlockMemberSymbol], val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol], val prevFnDefns: List[FunDefn], val prevClsDefns: List[ClsLikeDefn], @@ -95,7 +96,7 @@ class Lifter(using State): def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Nil, Nil, + def empty = LifterCtx(UsedLocalsMap(Map.empty), Set.empty, Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) @@ -197,6 +198,7 @@ class Lifter(using State): val reqdInnerSyms: List[InnerSymbol], val fakeCtorBms: Option[BlockMemberSymbol], // only for classes val singleCallBms: BlockMemberSymbol, // optimization + val isMod: Bool ) case class Lifted[+T <: Defn]( @@ -245,7 +247,11 @@ class Lifter(using State): val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) - val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms, singleCallBms) + val isMod = d match + case c: ClsLikeDefn => c.k is syntax.Mod + case _ => false + + val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms, singleCallBms, isMod) if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then Map.empty else d match @@ -399,7 +405,7 @@ class Lifter(using State): // deals with creating parameter lists def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match case N => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms, singleCallBms)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms, singleCallBms, isMod)) => val createSym = d match case d: ClsLikeDefn => // due to the possibility of capturing a TempSymbol in HandlerLowering, it is necessary to generate a discriminator @@ -949,26 +955,31 @@ class UsedVarAnalyzer(b: Block): defnSyms.get(l) match case None => super.applyValue(v) case Some(defn) => - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) - val muts = muted.intersect(thisVars) - val reads = defn.freeVars.intersect(thisVars) -- muts - // this is a naked reference, we assume things it mutates always needs a capture - for l <- muts do - reqCapture += l - hasMutator += l - for l <- reads do - if hasMutator.contains(l) then + val isMod = defn match + case c: ClsLikeDefn => c.k is syntax.Mod + case _ => false + if isMod then super.applyValue(v) + else + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val muts = muted.intersect(thisVars) + val reads = defn.freeVars.intersect(thisVars) -- muts + // this is a naked reference, we assume things it mutates always needs a capture + for l <- muts do 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 - // many mutators could be created from it - for - sym <- refd - l <- accessMap(sym).mutated - do - reqCapture += l - hasMutator += l + hasMutator += l + for l <- reads do + if hasMutator.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 + // many mutators could be created from it + for + sym <- refd + l <- accessMap(sym).mutated + do + reqCapture += l + hasMutator += l v case Value.Ref(l) => @@ -976,6 +987,12 @@ class UsedVarAnalyzer(b: Block): v case _ => super.applyValue(v) + override def applyDefn(defn: Defn): Defn = defn match + case c: ClsLikeDefn if c.k is syntax.Mod => + handleCalledBms(c.sym) + super.applyDefn(defn) + case _ => super.applyDefn(defn) + walker.applyBlock(b) CaptureInfo(reqCapture, hasReader, hasMutator) From 21219d36f2f94025b194924855be2d94fd7cbe6f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 02:52:09 +0800 Subject: [PATCH 060/127] add instance check test --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 74 +++++++++++++++++-- .../src/test/mlscript/lifter/ClassInFun.mls | 18 ++++- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index f7aa5591d..79903a381 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -5,6 +5,7 @@ import utils.* import hkmc2.codegen.* import hkmc2.semantics.* +import hkmc2.Message.* import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt @@ -44,7 +45,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(using State): +class Lifter(using State, Raise): import Lifter.* /** @@ -62,6 +63,7 @@ class Lifter(using State): case class LifterCtx( val usedLocals: UsedLocalsMap, val ignoredDefns: Set[BlockMemberSymbol], + val modules: Set[BlockMemberSymbol], val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol], val prevFnDefns: List[FunDefn], val prevClsDefns: List[ClsLikeDefn], @@ -82,7 +84,11 @@ class Lifter(using State): // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) def getIsymPath(l: InnerSymbol) = isymPaths.get(l) + def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) + def isModule(b: BlockMemberSymbol) = modules.contains(b) + def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) + def addModules(mods: Set[BlockMemberSymbol]) = copy(modules = mods ++ modules) def addFnDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) def addLocalCaptureSyms(m: Map[Local, LocalSymbol & NamedSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) @@ -96,8 +102,8 @@ class Lifter(using State): def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Set.empty, Map.empty, Nil, Nil, - Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) + def empty = LifterCtx(UsedLocalsMap(Map.empty), Set.empty, Set.empty, + Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) /** @@ -206,6 +212,62 @@ class Lifter(using State): val extraDefns: List[Defn], ) + // d is a top-level definition + // returns (unliftable classes, modules) + def createMetadata(d: Defn): (Set[BlockMemberSymbol], Set[BlockMemberSymbol]) = + var clsSymToBms: Map[Local, BlockMemberSymbol] = Map.empty + var modules: Set[BlockMemberSymbol] = Set.empty + + val walker = new BlockTransformer(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = + defn match + case c: ClsLikeDefn => + clsSymToBms += c.isym -> c.sym + if c.k is syntax.Mod then modules += c.sym + case _ => () + super.applyDefn(defn) + walker.applyDefn(d) + + val clsSyms = clsSymToBms.values.toSet + + var unliftable: Set[BlockMemberSymbol] = Set.empty + val walker2 = new BlockTransformer(SymbolSubst()): + override def applyCase(cse: Case): Case = + cse match + case Case.Cls(cls, path) => clsSymToBms.get(cls) match + case None => () + case Some(value) => + raise(WarningReport( + msg"Cannot yet lift the class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) + unliftable += value + case _ => () + cse + + override def applyResult(r: Result): Result = r match + case Call(Value.Ref(_: BlockMemberSymbol), args) => + args.map(applyArg) + r + case Instantiate(Select(Value.Ref(_: BlockMemberSymbol), Tree.Ident("class")), args) => + args.map(applyPath) + r + case _ => super.applyResult(r) + + override def applyValue(v: Value): Value = v match + case Value.Ref(l: BlockMemberSymbol) if clsSyms.contains(l) && !modules.contains(l) => + raise(WarningReport( + msg"Cannot yet lift the class `${l.nme}` as it is used as a higher-order class." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) + unliftable += l + v + case _ => super.applyValue(v) + walker2.applyDefn(d) + + (unliftable, modules) + + def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = /* the commented code is incorrect, more in-depth analysis is needed to properly remove variables/captures @@ -645,9 +707,11 @@ class Lifter(using State): val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => + val (unliftable, modules) = createMetadata(d) + val ctxxx = ctxx.addIgnored(unliftable).addModules(modules) val Lifted(lifted, extra) = d match - case f: FunDefn => liftDefnsInFn(f, ctxx) - case c: ClsLikeDefn => liftDefnsInCls(c, ctxx) + case f: FunDefn => liftDefnsInFn(f, ctxxx) + case c: ClsLikeDefn => liftDefnsInCls(c, ctxxx) case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 45fbad23f..6504ed184 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -85,6 +85,8 @@ f(1, 2).get() //│ runtime.safeCall(tmp1.get()) //│ = 1 +:todo +:w :expect 1 fun f(used1, unused1) = fun g(g_arg) = @@ -97,6 +99,7 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. //│ = 1 :expect 1 @@ -326,14 +329,23 @@ sum(100) // instance checks -:lift +:todo +:w fun foo(x, n) = class C() let res = if x is C then "Y" else "N" if n > 0 then res + foo(C(), n - 1) else "" +//│ ═══[WARNING] Cannot yet lift the class/module `C` as it is used in an instance check. + +:todo +:w +fun foo() = + class C() + C +//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a higher-order class. :todo :expect "NN" foo(0, 2) -//│ ═══[RUNTIME ERROR] Expected: '"NN"', got: '"NY"' -//│ = "NY" +//│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 +//│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' From c8e53f0c3f304655beb035f2e7cc4dd540b3aec3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 18:37:00 +0800 Subject: [PATCH 061/127] optimize and fix many problems --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 215 ++++++++++-------- hkmc2/shared/src/test/mlscript/HkScratch.mls | 4 +- .../test/mlscript/handlers/NestedHandlers.mls | 1 + .../src/test/mlscript/lifter/ClassInFun.mls | 86 ++++--- .../src/test/mlscript/lifter/FunInFun.mls | 28 ++- 5 files changed, 198 insertions(+), 136 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 79903a381..a72b011e3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -24,7 +24,10 @@ object Lifter: * @param vars The free variables that are accessed by nested classes/functions. * @param reqCapture The free variables that must be captured using a heap-allocated object. */ - case class FreeVars(vars: Set[Local], reqCapture: Set[Local]) + case class FreeVars(vars: Set[Local], reqCapture: Set[Local]): + def ++(that: FreeVars) = FreeVars(vars ++ that.vars, reqCapture ++ that.reqCapture) + object FreeVars: + val empty = FreeVars(Set.empty, Set.empty) /** * Describes the free variables of a function that have been accessed by nested definitions. @@ -37,9 +40,41 @@ object Lifter: // gets the function to which a local belongs def lookup(l: Local) = inverse.get(l) - def getVars(f: FunDefn): Set[Local] = - (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: - case s: FlowSymbol => s + object AccessInfo: + val empty = AccessInfo(Set.empty, Set.empty, Set.empty) + + case class AccessInfo( + accessed: Set[Local], + mutated: Set[Local], + refdDefns: Set[BlockMemberSymbol]): + def ++(that: AccessInfo) = AccessInfo( + accessed ++ that.accessed, + mutated ++ that.mutated, + refdDefns ++ that.refdDefns + ) + def withoutLocals(locals: Set[Local]) = AccessInfo( + accessed -- locals, + mutated -- locals, + refdDefns + ) + def intersectLocals(locals: Set[Local]) = AccessInfo( + accessed.intersect(locals), + mutated.intersect(locals), + refdDefns + ) + def addAccess(l: Local) = this.copy(accessed = accessed + l) + def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) + def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) + + def getVars(d: Defn)(using state: State): Set[Local] = d match + case f: FunDefn => + (f.body.definedVars ++ f.params.flatMap(_.paramSyms)).collect: + case s: FlowSymbol if !(s is state.runtimeSymbol) => s + case c: ClsLikeDefn => + (c.preCtor.definedVars ++ c.ctor.definedVars).collect: + case s: FlowSymbol if !(s is state.runtimeSymbol) => s + case _ => Set.empty + /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. @@ -52,7 +87,7 @@ class Lifter(using State, Raise): * The context of the class lifter. * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested defns * @param localCaptureSyms The symbols in a capture corresponding to a particular local - * @param prevFnDefns Function definitoins that have already been traversed + * @param prevFnLocals Locals belonging to function definitions that have already been traversed * @param prevClsDefns Class definitions that have already been traversed * @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 @@ -62,16 +97,17 @@ class Lifter(using State, Raise): */ case class LifterCtx( val usedLocals: UsedLocalsMap, + val accessInfo: Map[BlockMemberSymbol, AccessInfo], val ignoredDefns: Set[BlockMemberSymbol], val modules: Set[BlockMemberSymbol], val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol], - val prevFnDefns: List[FunDefn], + val prevFnLocals: FreeVars, val prevClsDefns: List[ClsLikeDefn], val capturePaths: Map[BlockMemberSymbol, Path], val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures val bmsPaths: Map[BlockMemberSymbol, Path], val localPaths: Map[Local, Local], - val isymPaths: Map[InnerSymbol, Local] + val isymPaths: Map[InnerSymbol, Local], ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -86,10 +122,12 @@ class Lifter(using State, Raise): def getIsymPath(l: InnerSymbol) = isymPaths.get(l) def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) def isModule(b: BlockMemberSymbol) = modules.contains(b) + def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) def addModules(mods: Set[BlockMemberSymbol]) = copy(modules = mods ++ modules) - def addFnDefn(f: FunDefn) = copy(prevFnDefns = f :: prevFnDefns) + def withAccesses(mp: Map[BlockMemberSymbol, AccessInfo]) = copy(accessInfo = mp) + def addFnLocals(f: FreeVars) = copy(prevFnLocals = prevFnLocals ++ f) def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) def addLocalCaptureSyms(m: Map[Local, LocalSymbol & NamedSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) def getBmsReqdInfo(sym: BlockMemberSymbol) = bmsReqdInfo.get(sym) @@ -102,8 +140,8 @@ class Lifter(using State, Raise): def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Set.empty, Set.empty, - Map.empty, Nil, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) + def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Set.empty, Set.empty, + Map.empty, FreeVars.empty, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) /** @@ -202,6 +240,7 @@ class Lifter(using State, Raise): val reqdCaptures: List[BlockMemberSymbol], val reqdVars: List[Local], val reqdInnerSyms: List[InnerSymbol], + val reqdBms: List[BlockMemberSymbol], // pass ignored blockmembersymbols val fakeCtorBms: Option[BlockMemberSymbol], // only for classes val singleCallBms: BlockMemberSymbol, // optimization val isMod: Bool @@ -265,7 +304,11 @@ class Lifter(using State, Raise): case _ => super.applyValue(v) walker2.applyDefn(d) - (unliftable, modules) + (unliftable, modules) + + extension (b: Block) + private def floatOut(ctx: LifterCtx) = + b.floatOutDefns(defn => ctx.ignored(defn.sym), defn => ctx.isModule(defn.sym)) def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = @@ -291,16 +334,14 @@ class Lifter(using State, Raise): val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) */ - // for now, we just include everything - val includedCaptures = ctx.prevFnDefns.filter: f => - val FreeVars(vars, cap) = ctx.usedLocals(f.sym) - cap.size != 0 + val AccessInfo(accessed, mutated, refdDefns)= ctx.getAccesses(d.sym) - val includedLocals = ctx.prevFnDefns.flatMap: f => - val FreeVars(vars, cap) = ctx.usedLocals(f.sym) - vars.filter(!cap.contains(_)) - .sortBy(_.uid) + val includedCaptures = ctx.prevFnLocals.reqCapture + .intersect(accessed) + .map(sym => ctx.lookup(sym).get) + .toList.sortBy(_.uid) + val includedLocals = (accessed -- ctx.prevFnLocals.reqCapture).toList.sortBy(_.uid) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) val fakeCtorBms = d match @@ -313,9 +354,14 @@ class Lifter(using State, Raise): case c: ClsLikeDefn => c.k is syntax.Mod case _ => false - val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures, fakeCtorBms, singleCallBms, isMod) + val info = LiftedInfo(includedCaptures, includedLocals, clsCaptures, Nil, fakeCtorBms, singleCallBms, isMod) - if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then Map.empty + if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then d match + case f: FunDefn => + createLiftInfoFn(f, parentCls, ctx) + case c: ClsLikeDefn => + createLiftInfoCls(c, ctx) + case _ => Map.empty else d match case f: FunDefn => createLiftInfoFn(f, parentCls, ctx) + (d.sym -> info) @@ -324,28 +370,14 @@ class Lifter(using State, Raise): case _ => Map.empty def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val (_, defns) = f.body.floatOutDefns() - defns.flatMap(createLiftInfoCont(_, N, ctx.addFnDefn(f))).toMap + val (_, defns) = f.body.floatOut(ctx) + defns.flatMap(createLiftInfoCont(_, N, ctx.addFnLocals(ctx.usedLocals(f.sym)))).toMap def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val defns = c.preCtor.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2 + val defns = c.preCtor.floatOut(ctx)._2 ++ c.ctor.floatOut(ctx)._2 val newCtx = ctx.addClsDefn(c) - defns.flatMap(f => createLiftInfoCont(f, N, newCtx)).toMap + defns.flatMap(f => createLiftInfoCont(f, N, newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, S(c), newCtx)) - - def createLiftInfo(b: Block, ctx: LifterCtx) = - var ret: Map[BlockMemberSymbol, LiftedInfo] = Map.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Define(f: FunDefn, rest) => - ret = ret ++ createLiftInfoFn(f, N, ctx) - super.applyBlock(b) - case Define(c: ClsLikeDefn, rest) => - ret = ret ++ createLiftInfoCls(c, ctx) - super.applyBlock(b) - case _ => super.applyBlock(b) - walker.applyBlock(b) - ret def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and @@ -356,7 +388,6 @@ class Lifter(using State, Raise): val walker = new BlockTransformerNoRec(SymbolSubst()): // only scan within the block. don't traverse - override def applyResult(r: Result): Result = r match // if possible, directly rewrite the call using the efficient version @@ -452,7 +483,7 @@ class Lifter(using State, Raise): def getCallArgs(sym: BlockMemberSymbol, ctx: LifterCtx) = val info = ctx.getBmsReqdInfo(sym).get - val localsArgs = info.reqdVars.map(ctx.getLocalPath(_).get.asPath.asArg) + val localsArgs = info.reqdVars.map(s => ctx.getLocalPath(s).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) iSymArgs ++ localsArgs ++ capturesArgs @@ -466,8 +497,11 @@ class Lifter(using State, Raise): // deals with creating parameter lists def liftOutDefnCont(base: Defn, d: Defn, ctx: LifterCtx): Lifted[Defn] = ctx.getBmsReqdInfo(d.sym) match - case N => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, fakeCtorBms, singleCallBms, isMod)) => + case N => d match + case f: FunDefn => liftDefnsInFn(f, ctx) + case c: ClsLikeDefn => liftDefnsInCls(c, ctx) + case _ => Lifted(d, Nil) + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms, isMod)) => val createSym = d match case d: ClsLikeDefn => // due to the possibility of capturing a TempSymbol in HandlerLowering, it is necessary to generate a discriminator @@ -603,8 +637,8 @@ class Lifter(using State, Raise): case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = - val (preCtor, preCtorDefns) = c.preCtor.floatOutDefns() - val (ctor, ctorDefns) = c.ctor.floatOutDefns() + val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctx) + val (ctor, ctorDefns) = c.ctor.floatOut(ctx) val newCtx = ctx.addIsymPath(c.isym, c.isym) // TODO: add block member symbol replacement @@ -638,14 +672,14 @@ class Lifter(using State, Raise): def liftDefnsInFn(f: FunDefn, ctx: LifterCtx): Lifted[FunDefn] = val (captureCls, varsMap, varsList) = createCaptureCls(f, ctx) - val (blk, nested) = f.body.floatOutDefns() + val (blk, nested) = f.body.floatOut(ctx) // add the mapping from this function's locals to the capture's symbols and the capture path val captureSym = FlowSymbol("capture") val captureCtx = ctx .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture - val nestedCtx = captureCtx.addFnDefn(f) + val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) // lift out the nested defns val nestedLifted = nested.map(liftOutDefnCont(f, _, nestedCtx)) @@ -701,17 +735,17 @@ class Lifter(using State, Raise): // top-level def transform(b: Block) = val blk = desugarLambdas(b) - val ctx = LifterCtx.withLocals(UsedVarAnalyzer(blk).findUsedLocals) - val ctxx = ctx.addBmsReqdInfo(createLiftInfo(blk, ctx)) - + val analyzer = UsedVarAnalyzer(blk) + val ctx = LifterCtx.withLocals(analyzer.findUsedLocals).withAccesses(analyzer.accessMap) + val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => val (unliftable, modules) = createMetadata(d) - val ctxxx = ctxx.addIgnored(unliftable).addModules(modules) + val ctxx = ctx.addIgnored(unliftable).addModules(modules) val Lifted(lifted, extra) = d match - case f: FunDefn => liftDefnsInFn(f, ctxxx) - case c: ClsLikeDefn => liftDefnsInCls(c, ctxxx) + case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, None, ctxx))) + case c: ClsLikeDefn => liftDefnsInCls(c, ctxx.addBmsReqdInfo(createLiftInfoCls(c, ctxx))) case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) @@ -724,32 +758,17 @@ class Lifter(using State, Raise): * * Assumes the input trees have no lambdas. */ -class UsedVarAnalyzer(b: Block): - import Lifter.FreeVars - - private case class AccessInfo( - accessed: Set[Local], - mutated: Set[Local], - refdDefns: Set[BlockMemberSymbol]): - def ++(that: AccessInfo) = AccessInfo( - accessed ++ that.accessed, - mutated ++ that.mutated, - refdDefns ++ that.refdDefns - ) - def addAccess(l: Local) = this.copy(accessed = accessed + l) - def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) - def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) - private object AccessInfo: - val empty = AccessInfo(Set.empty, Set.empty, Set.empty) +class UsedVarAnalyzer(b: Block)(using State): + import Lifter.* // 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 getDefinedLocals: (Map[BlockMemberSymbol, Set[Local]], Map[BlockMemberSymbol, Defn]) = + private def findDefinedLocals: (Map[BlockMemberSymbol, Set[Local]], Map[BlockMemberSymbol, Defn]) = var defnsMap: Map[BlockMemberSymbol, Defn] = Map.empty var usedMap: Map[BlockMemberSymbol, Set[Local]] = Map.empty - def getDefinedLocalsFn(f: FunDefn, existing: Set[Local]): Unit = + def findDefinedLocalsFn(f: FunDefn, existing: Set[Local]): Unit = val thisVars = Lifter.getVars(f) -- existing val newExisting = existing ++ thisVars @@ -757,31 +776,35 @@ class UsedVarAnalyzer(b: Block): usedMap += (f.sym -> thisVars) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - getDefinedLocalsDefn(defn, newExisting) + findDefinedLocalsDefn(defn, newExisting) defn walker.applyBlock(f.body) - def getDefinedLocalsDefn(d: Defn, existing: Set[Local]): Unit = + def findDefinedLocalsDefn(d: Defn, existing: Set[Local]): Unit = d match case f: FunDefn => - getDefinedLocalsFn(f, existing) + findDefinedLocalsFn(f, existing) case c: ClsLikeDefn => - getDefinedLocalsCls(c, existing) + findDefinedLocalsCls(c, existing) case d => Map.empty - def getDefinedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + def findDefinedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + val thisVars = Lifter.getVars(c) -- existing + val newExisting = existing ++ thisVars + defnsMap += (c.sym -> c) - val newExisting = existing ++ c.preCtor.definedVars ++ c.ctor.definedVars - for f <- c.methods do getDefinedLocalsFn(f, newExisting) + usedMap += (c.sym -> thisVars) + + for f <- c.methods do findDefinedLocalsFn(f, newExisting) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - getDefinedLocalsDefn(defn, b.definedVars) + findDefinedLocalsDefn(defn, b.definedVars) defn walker.applyBlock(b) (usedMap, defnsMap) - private val (definedLocals, defnsMap) = getDefinedLocals + private val (definedLocals, defnsMap) = findDefinedLocals private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -801,6 +824,7 @@ class UsedVarAnalyzer(b: Block): case _ => super.applyBlock(b) override def applyValue(v: Value): Value = v match + case Value.Ref(_: BuiltinSymbol) => super.applyValue(v) case Value.Ref(l: BlockMemberSymbol) => accessed = accessed.addRefdDefn(l); v case Value.Ref(l) => @@ -830,11 +854,11 @@ class UsedVarAnalyzer(b: Block): val ret = defn match case f: FunDefn => val fVars = definedLocals(f.sym) - blkAccessesShallow(f.body) + blkAccessesShallow(f.body).withoutLocals(fVars) case c: ClsLikeDefn => c.methods.foldLeft(blkAccessesShallow(c.preCtor) ++ blkAccessesShallow(c.ctor)): // here, we count the class as "accessing" all its methods (since they could be invoked anywhere) - case (acc, defn) => acc.addAccess(defn.sym) ++ findAccessesShallow(defn) + case (acc, defn) => acc.addRefdDefn(defn.sym) case _: ValDefn => AccessInfo.empty accessedCache.addOne(defn.sym -> ret) ret @@ -842,20 +866,31 @@ class UsedVarAnalyzer(b: Block): // MUST be called from a top-level defn private def findAccesses(f: FunDefn): Map[BlockMemberSymbol, AccessInfo] = var defns: List[Defn] = Nil + var definedVarsDeep: Set[Local] = definedLocals(f.sym) + var existingVarsAt: Map[BlockMemberSymbol, Set[Local]] = Map.empty + val walker = new BlockTransformer(SymbolSubst()): + override def applyFunDefn(f: FunDefn): FunDefn = + existingVarsAt += (f.sym -> definedVarsDeep) + defns +:= f; definedVarsDeep ++= definedLocals(f.sym) + super.applyFunDefn(f) + override def applyDefn(defn: Defn): Defn = + existingVarsAt += (defn.sym -> definedVarsDeep) defn match - case f: FunDefn => defns +:= f - case c: ClsLikeDefn => defns +:= c + case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) case _ => super.applyDefn(defn) walker.applyBlock(f.body) val defnSyms = defns.map(_.sym).toSet - val accessInfo = defns.map: d => - val accesses = findAccessesShallow(d) - d.sym -> accesses.copy(refdDefns = accesses.refdDefns.intersect(defnSyms)) + val AccessInfo(accessed, mutated, refdDefns) = findAccessesShallow(d) + d.sym -> AccessInfo( + accessed.intersect(definedVarsDeep), + mutated.intersect(definedVarsDeep), + refdDefns.intersect(defnSyms) + ) val accessInfoMap = accessInfo.toMap @@ -888,7 +923,7 @@ class UsedVarAnalyzer(b: Block): for (id, scc) <- sccs sym <- scc - yield sym -> sccAccessInfo(id) + yield sym -> (sccAccessInfo(id).intersectLocals(existingVarsAt(sym))) private def findAccessesTop = var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty @@ -902,7 +937,7 @@ class UsedVarAnalyzer(b: Block): walker.applyBlock(b) accessMap - private val accessMap = findAccessesTop + 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 diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..65d420d03 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -4,8 +4,8 @@ // :pt // :elt -:global -:d + + diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index 3f21cf79a..f0d4040c2 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -7,6 +7,7 @@ let id = 0 class MaybeStop with fun f(x: Bool): () + fun handleEffects(g) = handle h1 = MaybeStop with fun f(x)(k) = diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 6504ed184..25667803b 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -27,18 +27,18 @@ fun f(used1, unused1) = f(1, 2).get() //│ JS (unsanitized): //│ let h, Test1, g, f1, tmp1, Test$ctor, Test$, g$, h$; -//│ h$ = function h$(used1, used3) { +//│ h$ = function h$(used3) { //│ return used3 //│ }; -//│ h = function h(used1, used3) { +//│ h = function h(used3) { //│ return () => { -//│ return h$(used1, used3) +//│ return h$(used3) //│ } //│ }; //│ g$ = function g$(used1, g_arg) { //│ let used3, tmp2; //│ used3 = 2; -//│ tmp2 = h$(used1, used3); +//│ tmp2 = h$(used3); //│ return used1 + tmp2 //│ }; //│ g = function g(used1) { @@ -194,65 +194,61 @@ fun f() = f().foo() //│ JS (unsanitized): //│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; -//│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { +//│ Good$ = function Good$(x$1, y$2, f$capture$0) { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ return tmp5(x$1, y$2, f$capture$0) //│ }; -//│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { +//│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ return tmp5(x$1, y$2, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { -//│ return (x$11, y$21, z$31, f$capture$01) => { -//│ return new Good.class()(x$11, y$21, z$31, f$capture$01); +//│ return (x$11, y$21, f$capture$01) => { +//│ return new Good.class()(x$11, y$21, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (x$1, y$2, z$3, f$capture$0) => { +//│ return (x$1, y$2, f$capture$0) => { //│ this.x$1 = x$1; //│ this.y$2 = y$2; -//│ this.z$3 = z$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } //│ } //│ foo() { //│ let tmp5, tmp6; -//│ this.z$3 = 100; +//│ this.f$capture$0.z1$ = 100; //│ tmp5 = this.x$1 + this.y$2; -//│ tmp6 = tmp5 + this.z$3; +//│ tmp6 = tmp5 + this.f$capture$0.z1$; //│ return tmp6 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; -//│ Bad$ = function Bad$(x$1, y$2, z$3, f$capture$0) { +//│ Bad$ = function Bad$(f$capture$0) { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ return tmp5(f$capture$0) //│ }; -//│ Bad$ctor = function Bad$ctor(x$1, y$2, z$3, f$capture$0) { +//│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { //│ let tmp5; //│ tmp5 = new Bad1(); -//│ return tmp5(x$1, y$2, z$3, f$capture$0) +//│ return tmp5(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { -//│ return (x$11, y$21, z$31, f$capture$01) => { -//│ return new Bad.class()(x$11, y$21, z$31, f$capture$01); +//│ return (f$capture$01) => { +//│ return new Bad.class()(f$capture$01); //│ } //│ }; //│ Bad1.class = class Bad { //│ constructor() { -//│ return (x$1, y$2, z$3, f$capture$0) => { -//│ this.x$1 = x$1; -//│ this.y$2 = y$2; -//│ this.z$3 = z$3; +//│ return (f$capture$0) => { //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } @@ -263,25 +259,26 @@ f().foo() //│ } //│ toString() { return "Bad(" + "" + ")"; } //│ }; -//│ f$capture5 = function f$capture(w0$1) { -//│ return new f$capture.class(w0$1); +//│ f$capture5 = function f$capture(w0$1, z1$1) { +//│ return new f$capture.class(w0$1, z1$1); //│ }; //│ f$capture5.class = class f$capture4 { -//│ constructor(w0$) { +//│ constructor(w0$, z1$) { //│ this.w0$ = w0$; +//│ this.z1$ = z1$; //│ } -//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ", " + globalThis.Predef.render(this.z1$) + ")"; } //│ }; //│ f5 = function f() { -//│ let x, y, z, tmp5, tmp6, capture; -//│ capture = new f$capture5(null); +//│ let x, y, tmp5, tmp6, capture; +//│ capture = new f$capture5(null, null); //│ x = 1; //│ y = 10; -//│ z = 10; +//│ capture.z1$ = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(x, y, z, capture); +//│ tmp5 = Bad$(capture); //│ tmp6 = runtime.safeCall(tmp5.foo()); -//│ return Good$(x, y, z, capture) +//│ return Good$(x, y, capture) //│ }; //│ tmp4 = f5(); //│ runtime.safeCall(tmp4.foo()) @@ -349,3 +346,26 @@ fun foo() = foo(0, 2) //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 //│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' + +:fixme +:w +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + fun foo() = Test + foo()(unused1) +f(1, 2).get() +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test (class hkmc2.semantics.BlockMemberSymbol) + +:fixme +class Test +fun hello() = + class Test2 extends Test + 2 +//│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 9f9754169..96c5a850e 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -328,12 +328,12 @@ f(1, 2, 1000) //│ return g$6(immutable, f$capture6) //│ } //│ }; -//│ h$2 = function h$(immutable, f$capture6) { +//│ h$2 = function h$(f$capture6) { //│ return f$capture6.mutated0$ //│ }; -//│ h2 = function h(immutable, f$capture6) { +//│ h2 = function h(f$capture6) { //│ return () => { -//│ return h$2(immutable, f$capture6) +//│ return h$2(f$capture6) //│ } //│ }; //│ f$capture5 = function f$capture(mutated0$1) { @@ -350,7 +350,7 @@ f(1, 2, 1000) //│ capture = new f$capture5(mutated); //│ tmp15 = g$6(immutable, capture); //│ a1 = tmp15; -//│ tmp16 = h$2(immutable, capture); +//│ tmp16 = h$2(capture); //│ tmp17 = a1 + tmp16; //│ return tmp17 + unused //│ }; @@ -585,15 +585,10 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f20, g17, f22, f$2, g$13, f$capture15; -//│ g$13 = function g$(f$capture16) { +//│ let f20, g17, f22, f$2, f$capture15; +//│ g17 = function g() { //│ return 1 //│ }; -//│ g17 = function g(f$capture16) { -//│ return () => { -//│ return g$13(f$capture16) -//│ } -//│ }; //│ f$2 = function f$(f$capture16) { //│ f$capture16.x0$ = 1; //│ return runtime.Unit @@ -621,3 +616,14 @@ f() //│ }; //│ f22() //│ = 1 + +let n = 0 +//│ n = 0 + +fun h() = 3 + +fun f() = + fun g() = + set n = n + 1 + g() +f() From 5ba26236c8443826dbadb267d2fcf58540e8c382 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 18:39:34 +0800 Subject: [PATCH 062/127] fix test and add test --- .../test/mlscript/handlers/NestedHandlers.mls | 1 - .../test/mlscript/lifter/StackSafetyLift.mls | 736 ++++++++++++++++++ 2 files changed, 736 insertions(+), 1 deletion(-) create mode 100644 hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls diff --git a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls index f0d4040c2..3f21cf79a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/NestedHandlers.mls @@ -7,7 +7,6 @@ let id = 0 class MaybeStop with fun f(x: Bool): () - fun handleEffects(g) = handle h1 = MaybeStop with fun f(x)(k) = diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls new file mode 100644 index 000000000..6d5883838 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -0,0 +1,736 @@ +:js +:lift + +// sanity check +:expect 5050 +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +sum(100) +//│ = 5050 + +// preserve tail calls +// MUST see "return hi1(tmp)" in the output +:stackSafe 5 +:effectHandlers +:expect 0 +:sjs +fun hi(n) = + if n == 0 then 0 + else hi(n - 1) +hi(0) +//│ JS (unsanitized): +//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, hi$capture1, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, handleBlock$$capture1, lambda$capture1; +//│ Cont$$ = function Cont$$(n$1, hi$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$3(pc); +//│ return tmp(n$1, hi$capture$0) +//│ }; +//│ Cont$$ctor = function Cont$$ctor(n$1, hi$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$3(pc); +//│ return tmp(n$1, hi$capture$0) +//│ } +//│ }; +//│ Cont$3 = function Cont$(pc1) { +//│ return (n$11, hi$capture$01) => { +//│ return new Cont$.class(pc1)(n$11, hi$capture$01); +//│ } +//│ }; +//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (n$1, hi$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.n$1 = n$1; +//│ this.hi$capture$0 = hi$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.hi$capture$0.stackDelayRes0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ this.hi$capture$0.scrut1$ = this.n$1 == 0; +//│ if (this.hi$capture$0.scrut1$ === true) { +//│ return 0 +//│ } else { +//│ this.hi$capture$0.tmp2$ = this.n$1 - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ return hi(this.hi$capture$0.tmp2$) +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } else if (this.pc === 1) { +//│ break contLoop; +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ hi$capture1 = function hi$capture(stackDelayRes0$1, scrut1$1, tmp2$1) { +//│ return new hi$capture.class(stackDelayRes0$1, scrut1$1, tmp2$1); +//│ }; +//│ hi$capture1.class = class hi$capture { +//│ constructor(stackDelayRes0$, scrut1$, tmp2$) { +//│ this.stackDelayRes0$ = stackDelayRes0$; +//│ this.scrut1$ = scrut1$; +//│ this.tmp2$ = tmp2$; +//│ } +//│ toString() { return "hi$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.scrut1$) + ", " + globalThis.Predef.render(this.tmp2$) + ")"; } +//│ }; +//│ hi = function hi(n) { +//│ let capture; +//│ capture = new hi$capture1(null, null, null); +//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); +//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.stackDelayRes0$.tail.next = Cont$$(n, capture, 0); +//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; +//│ return capture.stackDelayRes0$ +//│ } +//│ capture.scrut1$ = n == 0; +//│ if (capture.scrut1$ === true) { +//│ return 0 +//│ } else { +//│ capture.tmp2$ = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ return hi(capture.tmp2$) +//│ } +//│ }; +//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { +//│ let tmp; +//│ tmp = new StackDelay$1(); +//│ return tmp(handleBlock$$capture$0) +//│ }; +//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { +//│ return () => { +//│ let tmp; +//│ tmp = new StackDelay$1(); +//│ return tmp(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$$2 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$5(pc); +//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ }; +//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$5(pc); +//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ } +//│ }; +//│ Cont$5 = function Cont$(pc1) { +//│ return (StackDelay$$instance$11, lambda$capture$01) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$11, lambda$capture$01); +//│ } +//│ }; +//│ Cont$5.class = class Cont$1 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (StackDelay$$instance$1, lambda$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.StackDelay$$instance$1 = StackDelay$$instance$1; +//│ this.lambda$capture$0 = lambda$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 4) { +//│ this.lambda$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 4) { +//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ this.pc = 5; +//│ continue contLoop; +//│ } else if (this.pc === 5) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ lambda$capture1 = function lambda$capture(res0$1) { +//│ return new lambda$capture.class(res0$1); +//│ }; +//│ lambda$capture1.class = class lambda$capture { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ lambda$ = function lambda$(StackDelay$$instance, resume) { +//│ let capture; +//│ capture = new lambda$capture1(null); +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ capture.res0$ = resume(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$2(StackDelay$$instance, capture, 4); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$ +//│ } +//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res0$ +//│ } +//│ return capture.res0$ +//│ }; +//│ lambda = function lambda(StackDelay$$instance) { +//│ return (resume) => { +//│ return lambda$(StackDelay$$instance, resume) +//│ } +//│ }; +//│ StackDelay$1 = function StackDelay$() { +//│ return (handleBlock$$capture$01) => { +//│ return new StackDelay$.class()(handleBlock$$capture$01); +//│ } +//│ }; +//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { +//│ constructor() { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(); +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda(this)); +//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ Cont$$1 = function Cont$$(handleBlock$$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$4(pc); +//│ return tmp(handleBlock$$capture$0) +//│ }; +//│ Cont$$ctor1 = function Cont$$ctor(handleBlock$$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$4(pc); +//│ return tmp(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$4 = function Cont$(pc1) { +//│ return (handleBlock$$capture$01) => { +//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ } +//│ }; +//│ Cont$4.class = class Cont$2 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 2) { +//│ this.handleBlock$$capture$0.res1$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 2) { +//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ this.pc = 3; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$$capture1 = function handleBlock$$capture(stackHandler0$1, res1$1) { +//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ }; +//│ handleBlock$$capture1.class = class handleBlock$$capture { +//│ constructor(stackHandler0$, res1$) { +//│ this.stackHandler0$ = stackHandler0$; +//│ this.res1$ = res1$; +//│ } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let capture; +//│ capture = new handleBlock$$capture1(null, null); +//│ capture.stackHandler0$ = StackDelay$$(capture); +//│ globalThis.Predef.__stackLimit = 5; +//│ globalThis.Predef.__stackOffset = 0; +//│ globalThis.Predef.__stackDepth = 1; +//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; +//│ capture.res1$ = hi(0); +//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res1$.tail.next = Cont$$1(capture, 2); +//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ } +//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res1$ +//│ } +//│ return capture.res1$ +//│ }; +//│ res = handleBlock$(); +//│ if (res instanceof this.Predef.__EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ this.Predef.__stackDepth = 0; +//│ this.Predef.__stackHandler = null; +//│ res +//│ = 0 + +:sjs +:stackSafe 1000 +:effectHandlers +:expect 50005000 +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +sum(10000) +//│ JS (unsanitized): +//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, sum$capture1, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, handleBlock$$capture3, lambda$capture3; +//│ Cont$$3 = function Cont$$(n$1, curDepth$2, sum$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$9(pc); +//│ return tmp(n$1, curDepth$2, sum$capture$0) +//│ }; +//│ Cont$$ctor3 = function Cont$$ctor(n$1, curDepth$2, sum$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$9(pc); +//│ return tmp(n$1, curDepth$2, sum$capture$0) +//│ } +//│ }; +//│ Cont$9 = function Cont$(pc1) { +//│ return (n$11, curDepth$21, sum$capture$01) => { +//│ return new Cont$.class(pc1)(n$11, curDepth$21, sum$capture$01); +//│ } +//│ }; +//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (n$1, curDepth$2, sum$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.n$1 = n$1; +//│ this.curDepth$2 = curDepth$2; +//│ this.sum$capture$0 = sum$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.sum$capture$0.stackDelayRes3$ = value$; +//│ } else if (this.pc === 1) { +//│ this.sum$capture$0.tmp0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ this.sum$capture$0.scrut1$ = this.n$1 == 0; +//│ if (this.sum$capture$0.scrut1$ === true) { +//│ return 0 +//│ } else { +//│ this.sum$capture$0.tmp2$ = this.n$1 - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ this.sum$capture$0.tmp0$ = sum1(this.sum$capture$0.tmp2$); +//│ if (this.sum$capture$0.tmp0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ this.pc = 1; +//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp0$, this) +//│ } +//│ this.pc = 1; +//│ continue contLoop; +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ break contLoop; +//│ } else if (this.pc === 1) { +//│ this.sum$capture$0.tmp0$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp0$, this.curDepth$2); +//│ return this.n$1 + this.sum$capture$0.tmp0$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ sum$capture1 = function sum$capture(tmp0$1, scrut1$1, tmp2$1, stackDelayRes3$1) { +//│ return new sum$capture.class(tmp0$1, scrut1$1, tmp2$1, stackDelayRes3$1); +//│ }; +//│ sum$capture1.class = class sum$capture { +//│ constructor(tmp0$, scrut1$, tmp2$, stackDelayRes3$) { +//│ this.tmp0$ = tmp0$; +//│ this.scrut1$ = scrut1$; +//│ this.tmp2$ = tmp2$; +//│ this.stackDelayRes3$ = stackDelayRes3$; +//│ } +//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.tmp0$) + ", " + globalThis.Predef.render(this.scrut1$) + ", " + globalThis.Predef.render(this.tmp2$) + ", " + globalThis.Predef.render(this.stackDelayRes3$) + ")"; } +//│ }; +//│ sum1 = function sum(n) { +//│ let curDepth, capture; +//│ capture = new sum$capture1(null, null, null, null); +//│ curDepth = globalThis.Predef.__stackDepth; +//│ capture.stackDelayRes3$ = globalThis.Predef.checkDepth(); +//│ if (capture.stackDelayRes3$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.stackDelayRes3$.tail.next = Cont$$3(n, curDepth, capture, 0); +//│ capture.stackDelayRes3$.tail = capture.stackDelayRes3$.tail.next; +//│ return capture.stackDelayRes3$ +//│ } +//│ capture.scrut1$ = n == 0; +//│ if (capture.scrut1$ === true) { +//│ return 0 +//│ } else { +//│ capture.tmp2$ = n - 1; +//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ capture.tmp0$ = sum1(capture.tmp2$); +//│ if (capture.tmp0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.tmp0$.tail.next = Cont$$3(n, curDepth, capture, 1); +//│ capture.tmp0$.tail = capture.tmp0$.tail.next; +//│ return capture.tmp0$ +//│ } +//│ capture.tmp0$ = globalThis.Predef.resetDepth(capture.tmp0$, curDepth); +//│ return n + capture.tmp0$ +//│ } +//│ }; +//│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { +//│ let tmp; +//│ tmp = new StackDelay$3(); +//│ return tmp(handleBlock$$capture$0) +//│ }; +//│ StackDelay$$ctor1 = function StackDelay$$ctor(handleBlock$$capture$0) { +//│ return () => { +//│ let tmp; +//│ tmp = new StackDelay$3(); +//│ return tmp(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$$5 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$11(pc); +//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ }; +//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$11(pc); +//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ } +//│ }; +//│ Cont$11 = function Cont$(pc1) { +//│ return (StackDelay$$instance$11, lambda$capture$01) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$11, lambda$capture$01); +//│ } +//│ }; +//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (StackDelay$$instance$1, lambda$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.StackDelay$$instance$1 = StackDelay$$instance$1; +//│ this.lambda$capture$0 = lambda$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 5) { +//│ this.lambda$capture$0.res0$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 5) { +//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ this.pc = 6; +//│ continue contLoop; +//│ } else if (this.pc === 6) { +//│ return this.lambda$capture$0.res0$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ lambda$capture3 = function lambda$capture(res0$1) { +//│ return new lambda$capture.class(res0$1); +//│ }; +//│ lambda$capture3.class = class lambda$capture2 { +//│ constructor(res0$) { +//│ this.res0$ = res0$; +//│ } +//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } +//│ }; +//│ lambda$1 = function lambda$(StackDelay$$instance, resume) { +//│ let capture; +//│ capture = new lambda$capture3(null); +//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; +//│ capture.res0$ = resume(); +//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, capture, 5); +//│ capture.res0$.tail = capture.res0$.tail.next; +//│ return capture.res0$ +//│ } +//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res0$ +//│ } +//│ return capture.res0$ +//│ }; +//│ lambda1 = function lambda(StackDelay$$instance) { +//│ return (resume) => { +//│ return lambda$1(StackDelay$$instance, resume) +//│ } +//│ }; +//│ StackDelay$3 = function StackDelay$() { +//│ return (handleBlock$$capture$01) => { +//│ return new StackDelay$.class()(handleBlock$$capture$01); +//│ } +//│ }; +//│ StackDelay$3.class = class StackDelay$2 extends globalThis.Predef.__StackDelay { +//│ constructor() { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(); +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda1(this)); +//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ } +//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ }; +//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { +//│ let tmp; +//│ tmp = new Cont$10(pc); +//│ return tmp(handleBlock$$capture$0) +//│ }; +//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { +//│ return (pc) => { +//│ let tmp; +//│ tmp = new Cont$10(pc); +//│ return tmp(handleBlock$$capture$0) +//│ } +//│ }; +//│ Cont$10 = function Cont$(pc1) { +//│ return (handleBlock$$capture$01) => { +//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ } +//│ }; +//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { +//│ constructor(pc) { +//│ return (handleBlock$$capture$0) => { +//│ let tmp; +//│ tmp = super(null, null); +//│ this.pc = pc; +//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 3) { +//│ this.handleBlock$$capture$0.res1$ = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 3) { +//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ this.pc = 4; +//│ continue contLoop; +//│ } else if (this.pc === 4) { +//│ return this.handleBlock$$capture$0.res1$ +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1, res1$1) { +//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ }; +//│ handleBlock$$capture3.class = class handleBlock$$capture2 { +//│ constructor(stackHandler0$, res1$) { +//│ this.stackHandler0$ = stackHandler0$; +//│ this.res1$ = res1$; +//│ } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ }; +//│ handleBlock$1 = function handleBlock$() { +//│ let capture; +//│ capture = new handleBlock$$capture3(null, null); +//│ capture.stackHandler0$ = StackDelay$$1(capture); +//│ globalThis.Predef.__stackLimit = 1000; +//│ globalThis.Predef.__stackOffset = 0; +//│ globalThis.Predef.__stackDepth = 1; +//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; +//│ capture.res1$ = sum1(10000); +//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.res1$.tail.next = Cont$$4(capture, 3); +//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ } +//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { +//│ return capture.res1$ +//│ } +//│ return capture.res1$ +//│ }; +//│ res1 = handleBlock$1(); +//│ if (res1 instanceof this.Predef.__EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ this.Predef.__stackDepth = 0; +//│ this.Predef.__stackHandler = null; +//│ res1 +//│ = 50005000 + +// stack-overflows without :stackSafe +:re +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +sum(10000) +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded + +:effectHandlers +:stackSafe 100 +mut val ctr = 0 +fun dummy(x) = x +fun foo(f) = + if ctr > 10000 then 0 + else + set ctr += 1 + dummy(f(f)) +foo(foo) +//│ = 0 +//│ ctr = 10001 + +:stackSafe 1000 +:effectHandlers +:expect 50005000 +val foo = + val f = n => + if n <= 0 then 0 + else n + f(n-1) + f(10000) +foo +//│ = 50005000 +//│ foo = 50005000 + +:re +fun foo() = + let f = () + set f = n => + if n <= 0 then 0 + else n + f(n-1) + f(10000) +foo() +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded + +abstract class Eff with + fun perform(a): () + +// functions and lambdas inside handlers +:effectHandlers +:stackSafe 100 +:expect 50005000 +fun foo(h) = h.perform +handle h = Eff with + fun perform(resume) = + let f = () + set f = n => + if n <= 0 then 0 + else n + f(n-1) + resume(f(10000)) +foo(h) +//│ = 50005000 + +// function call and defn inside handler +:effectHandlers +:stackSafe 100 +:expect 50005000 +handle h = Eff with + fun perform(resume) = + let f = () + set f = n => + if n <= 0 then 0 + else n + f(n-1) + resume(f(10000)) +in + fun foo(h) = h.perform + foo(h) +//│ = 50005000 + +:re +:effectHandlers +fun foo(h) = h.perform(2) +handle h = Eff with + fun perform(a)(resume) = + let f = () + set f = n => + if n <= 0 then 0 + else n + f(n-1) + resume(f(10000)) +foo(h) +//│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded + +:effectHandlers +:stackSafe +:sjs +fun max(a, b) = if a < b then b else a +//│ JS (unsanitized): +//│ let max; +//│ max = function max(a, b) { +//│ let scrut; +//│ scrut = a < b; +//│ if (scrut === true) { +//│ return b +//│ } else { +//│ return a +//│ } +//│ }; + + +:sjs +:stackSafe 42 +fun hi(n) = n +hi(0) +//│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set +//│ JS (unsanitized): +//│ let hi1; hi1 = function hi(n) { return n }; hi1(0) +//│ = 0 + +:stackSafe 42 +hi(0) +//│ /!!!\ Option ':stackSafe' requires ':effectHandlers' to be set +//│ = 0 + + +:stackSafe 1000 +:effectHandlers +:expect 100010000 +fun sum(n) = + if n == 0 then 0 + else + n + sum(n - 1) +fun bad() = sum(10000) + sum(10000) +bad() +//│ = 100010000 + From d2129d543c7427e257f3e67e8700982de4038385 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 9 Feb 2025 20:04:47 +0800 Subject: [PATCH 063/127] properly handle ignored classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 91 ++++++++++++------- hkmc2/shared/src/test/mlscript/HkScratch.mls | 4 +- .../src/test/mlscript/lifter/ClassInFun.mls | 18 ++-- 3 files changed, 66 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index a72b011e3..93f9c1880 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -91,7 +91,7 @@ class Lifter(using State, Raise): * @param prevClsDefns Class definitions that have already been traversed * @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 bmsPaths The path to access a particular BlockMemberSymbol in the local scope with the captures already applied + * @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 */ @@ -105,7 +105,7 @@ class Lifter(using State, Raise): val prevClsDefns: List[ClsLikeDefn], val capturePaths: Map[BlockMemberSymbol, Path], val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures - val bmsPaths: Map[BlockMemberSymbol, Path], + val ignoredBmsPaths: Map[BlockMemberSymbol, Local], val localPaths: Map[Local, Local], val isymPaths: Map[InnerSymbol, Local], ): @@ -120,6 +120,7 @@ class Lifter(using State, Raise): // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(l) def getIsymPath(l: InnerSymbol) = isymPaths.get(l) + def getIgnoredBmsPath(b: BlockMemberSymbol) = ignoredBmsPaths.get(b) def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) def isModule(b: BlockMemberSymbol) = modules.contains(b) def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) @@ -135,8 +136,10 @@ class Lifter(using State, Raise): def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) + def replIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = m) def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(isymPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) + def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: @@ -308,7 +311,7 @@ class Lifter(using State, Raise): extension (b: Block) private def floatOut(ctx: LifterCtx) = - b.floatOutDefns(defn => ctx.ignored(defn.sym), defn => ctx.isModule(defn.sym)) + b.floatOutDefns(preserve = defn => ctx.isModule(defn.sym) || ctx.ignored(defn.sym)) def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = @@ -343,31 +346,37 @@ class Lifter(using State, Raise): val includedLocals = (accessed -- ctx.prevFnLocals.reqCapture).toList.sortBy(_.uid) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) - - val fakeCtorBms = d match - case c: ClsLikeDefn => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) - case _ => N - - val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) + val refBms = refdDefns.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) val isMod = d match case c: ClsLikeDefn => c.k is syntax.Mod case _ => false - val info = LiftedInfo(includedCaptures, includedLocals, clsCaptures, Nil, fakeCtorBms, singleCallBms, isMod) + if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty then + d match + case f: FunDefn => + createLiftInfoFn(f, parentCls, ctx) + case c: ClsLikeDefn => + createLiftInfoCls(c, ctx) + case _ => Map.empty + else + val fakeCtorBms = d match + case c: ClsLikeDefn => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) + case _ => N + + val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) - if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty then d match - case f: FunDefn => - createLiftInfoFn(f, parentCls, ctx) - case c: ClsLikeDefn => - createLiftInfoCls(c, ctx) - case _ => Map.empty - else d match - case f: FunDefn => - createLiftInfoFn(f, parentCls, ctx) + (d.sym -> info) - case c: ClsLikeDefn => - createLiftInfoCls(c, ctx) + (d.sym -> info) - case _ => Map.empty + val info = LiftedInfo( + includedCaptures, includedLocals, clsCaptures, + refBms, fakeCtorBms, singleCallBms, isMod + ) + + d match + case f: FunDefn => + createLiftInfoFn(f, parentCls, ctx) + (d.sym -> info) + case c: ClsLikeDefn => + createLiftInfoCls(c, ctx) + (d.sym -> info) + case _ => Map.empty def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOut(ctx) @@ -450,11 +459,9 @@ class Lifter(using State, Raise): case _ => super.applyBlock(b) override def applyPath(p: Path): Path = p match - /* - case Value.Ref(b: BlockMemberSymbol) => newCtx.getBmsPath(b) match - case None => super.applyPath(p) - case Some(value) => value - */ + case Value.Ref(b: BlockMemberSymbol) => ctx.getIgnoredBmsPath(b) match + case Some(value) => Value.Ref(value) + case _ => super.applyPath(p) case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match case Some(value) if !belongsToCtor(value) => Value.Ref(value) case _ => super.applyPath(p) @@ -486,7 +493,8 @@ class Lifter(using State, Raise): val localsArgs = info.reqdVars.map(s => ctx.getLocalPath(s).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) - iSymArgs ++ localsArgs ++ capturesArgs + val bmsArgs = info.reqdBms.map(ctx.getIgnoredBmsPath(_).get.asPath.asArg) + bmsArgs ++ iSymArgs ++ localsArgs ++ capturesArgs def createCall(sym: BlockMemberSymbol, ctx: LifterCtx): Call = val info = ctx.getBmsReqdInfo(sym).get @@ -514,12 +522,15 @@ class Lifter(using State, Raise): val capturesSymbols = includedCaptures.map: sym => (sym, createSym(sym.nme + "$capture")) - val localsSymbols = includedLocals.map:sym => + val localsSymbols = includedLocals.map: sym => (sym, createSym(sym.nme)) - val isymSymbols = clsCaptures.map:sym => + 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, None) val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol @@ -538,12 +549,19 @@ class Lifter(using State, Raise): case (d, sym) => d -> sym .toMap - val extraParams = extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures + val extraParamsBms = bmsSymbols.map: // parameter list + case (d, sym) => Param(FldFlags.empty, sym, None) + val newBmsPaths = bmsSymbols.map: // mapping from sym to param symbol + case (d, sym) => d -> sym + .toMap + + val extraParams = extraParamsBms ++ extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures val newCtx = ctx .replCapturePaths(newCapturePaths) .replLocalPaths(newLocalsPaths) .replIsymPaths(newIsymPaths) + .replIgnoredBmsPaths(newBmsPaths) d match case f: FunDefn => @@ -646,7 +664,11 @@ class Lifter(using State, Raise): val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) val newCtor = rewriteBlk(ctor, S(c), newCtx) - val ctorDefnsLifted = (preCtorDefns ++ ctorDefns).flatMap: defn => + val allCtorDefns = preCtorDefns ++ ctorDefns + + val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) + + val ctorDefnsLifted = ctorIncluded.flatMap: defn => val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) liftedDefn :: extraDefns @@ -674,6 +696,8 @@ class Lifter(using State, Raise): val (blk, nested) = f.body.floatOut(ctx) + val (ignored, included) = nested.partition(d => ctx.ignored(d.sym)) + // add the mapping from this function's locals to the capture's symbols and the capture path val captureSym = FlowSymbol("capture") val captureCtx = ctx @@ -682,7 +706,7 @@ class Lifter(using State, Raise): val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) // lift out the nested defns - val nestedLifted = nested.map(liftOutDefnCont(f, _, nestedCtx)) + val nestedLifted = included.map(liftOutDefnCont(f, _, nestedCtx)) val newDefns = nestedLifted.flatMap: case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns @@ -690,6 +714,7 @@ class Lifter(using State, Raise): val thisVars = ctx.usedLocals(f.sym) val newCtx = captureCtx .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> s).toMap) + .addIgnoredBmsPaths(ignored.map(d => d.sym -> d.sym).toMap) val transformed = rewriteBlk(blk, N, newCtx) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index 65d420d03..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -4,8 +4,8 @@ // :pt // :elt - - +:global +:d diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 25667803b..c6d523a48 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -100,7 +100,7 @@ fun f(used1, unused1) = foo(unused1) f(1, 2).get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ = 1 +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test$ctor (class hkmc2.semantics.BlockMemberSymbol) :expect 1 fun f(used1, unused1) = @@ -347,21 +347,15 @@ foo(0, 2) //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 //│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' -:fixme :w -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 +fun f() = class Test(a) with - fun get() = used1 + fun get() = a fun foo() = Test - foo()(unused1) -f(1, 2).get() + foo()(0) +f().get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test (class hkmc2.semantics.BlockMemberSymbol) +//│ = 0 :fixme class Test From 8bde300f4a5453e95acd7f6a3233539c85b62f53 Mon Sep 17 00:00:00 2001 From: Anson Yeung Date: Sun, 9 Feb 2025 23:21:21 +0800 Subject: [PATCH 064/127] Add todo --- .../src/test/mlscript/lifter/ClassInFun.mls | 85 +++++++++++-------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index c6d523a48..fea06468b 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -115,6 +115,21 @@ fun f(used1, unused1) = f(1, 2).get() //│ = 1 +:todo +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test with + fun get() = used1 + new Test +f(1, 2).get() +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test$ctor (class hkmc2.semantics.BlockMemberSymbol) + :sjs :expect 2 fun f(x) = @@ -124,17 +139,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f4, A$ctor, A$, f$capture3; +//│ let A1, f5, A$ctor, A$, f$capture3; //│ A$ = function A$(f$capture$0) { -//│ let tmp4; -//│ tmp4 = new A1(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1(); +//│ return tmp5(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp4; -//│ tmp4 = new A1(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1(); +//│ return tmp5(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -164,14 +179,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f4 = function f(x) { -//│ let tmp4, tmp5, capture; +//│ f5 = function f(x) { +//│ let tmp5, tmp6, capture; //│ capture = new f$capture3(x); -//│ tmp4 = A$(capture); -//│ tmp5 = runtime.safeCall(tmp4.f()); +//│ tmp5 = A$(capture); +//│ tmp6 = runtime.safeCall(tmp5.f()); //│ return capture.x0$ //│ }; -//│ f4(1) +//│ f5(1) //│ = 2 // only w should be in a capture @@ -193,17 +208,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; +//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; //│ Good$ = function Good$(x$1, y$2, f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1(); +//│ return tmp6(x$1, y$2, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1(); +//│ return tmp6(x$1, y$2, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -221,24 +236,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp5, tmp6; +//│ let tmp6, tmp7; //│ this.f$capture$0.z1$ = 100; -//│ tmp5 = this.x$1 + this.y$2; -//│ tmp6 = tmp5 + this.f$capture$0.z1$; -//│ return tmp6 + this.f$capture$0.w0$ +//│ tmp6 = this.x$1 + this.y$2; +//│ tmp7 = tmp6 + this.f$capture$0.z1$; +//│ return tmp7 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Bad1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1(); +//│ return tmp6(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Bad1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1(); +//│ return tmp6(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -269,19 +284,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ", " + globalThis.Predef.render(this.z1$) + ")"; } //│ }; -//│ f5 = function f() { -//│ let x, y, tmp5, tmp6, capture; +//│ f6 = function f() { +//│ let x, y, tmp6, tmp7, capture; //│ capture = new f$capture5(null, null); //│ x = 1; //│ y = 10; //│ capture.z1$ = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(capture); -//│ tmp6 = runtime.safeCall(tmp5.foo()); +//│ tmp6 = Bad$(capture); +//│ tmp7 = runtime.safeCall(tmp6.foo()); //│ return Good$(x, y, capture) //│ }; -//│ tmp4 = f5(); -//│ runtime.safeCall(tmp4.foo()) +//│ tmp5 = f6(); +//│ runtime.safeCall(tmp5.foo()) //│ = 10111 :fixme From beca9775b9bba717fb9b50015efbe9fc69f95bda Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 00:07:19 +0800 Subject: [PATCH 065/127] modules almost working --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 308 ++++++++++-------- .../scala/hkmc2/codegen/js/JSBuilder.scala | 3 +- .../src/test/mlscript/lifter/ClassInClass.mls | 4 + .../src/test/mlscript/lifter/ClassInFun.mls | 211 ++++++++---- .../src/test/mlscript/lifter/Modules.mls | 67 ++++ 5 files changed, 410 insertions(+), 183 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls create mode 100644 hkmc2/shared/src/test/mlscript/lifter/Modules.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 93f9c1880..793c9a951 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -43,6 +43,13 @@ object Lifter: object AccessInfo: val empty = AccessInfo(Set.empty, Set.empty, Set.empty) + /** + * Describes previously defined locals and definitions which could possibly be accessed or mutated by a definition. + * + * @param accessed Previously defined locals which could possibly be accessed or mutated. + * @param mutated Such locals which could also be mutated by this definition. + * @param refdDefns Previously defined definitions which could possibly be used by this definition. + */ case class AccessInfo( accessed: Set[Local], mutated: Set[Local], @@ -62,6 +69,11 @@ object Lifter: mutated.intersect(locals), refdDefns ) + def withoutBms(locals: Set[BlockMemberSymbol]) = AccessInfo( + accessed, + mutated, + refdDefns -- locals + ) def addAccess(l: Local) = this.copy(accessed = accessed + l) def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) @@ -84,8 +96,13 @@ class Lifter(using State, Raise): import Lifter.* /** - * The context of the class lifter. - * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested defns + * The context of the class lifter. One can create an empty context using `Lifter.empty`. + * + * @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 modules The modules in the block to be lifted. * @param localCaptureSyms The symbols in a capture corresponding to a particular local * @param prevFnLocals Locals belonging to function definitions that have already been traversed * @param prevClsDefns Class definitions that have already been traversed @@ -139,6 +156,7 @@ class Lifter(using State, Raise): def replIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = m) def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(isymPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) + def addLocalPath(target: Local, path: Local) = copy(localPaths = localPaths + (target -> path)) def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) @@ -246,7 +264,7 @@ class Lifter(using State, Raise): val reqdBms: List[BlockMemberSymbol], // pass ignored blockmembersymbols val fakeCtorBms: Option[BlockMemberSymbol], // only for classes val singleCallBms: BlockMemberSymbol, // optimization - val isMod: Bool + val modLocal: Opt[FlowSymbol] // for modules ) case class Lifted[+T <: Defn]( @@ -315,28 +333,6 @@ class Lifter(using State, Raise): def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - /* - the commented code is incorrect, more in-depth analysis is needed to properly remove variables/captures - that aren't needed. For example, - fun f() = - fun g(x) = x - fun h(y) = y - both g and h both capture x and y, but this is not needed. - - Incorrect code: - - val includedCaptures = ctx.prevFnDefns.filter(needsCapture(_, d, ctx)) - - val includedLocals = ctx.prevFnDefns.flatMap: ls => - neededImutLocals(ls, d, ctx) - - val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.filter(needsClsCapture(_, d)).map(_.isym).collect: - // this line is just to satisfy the type system, in reality anything we capture is an InnerSymbol - case s: InnerSymbol => s - - val info = LiftedInfo(includedCaptures.map(_.sym), includedLocals, clsCaptures) - */ - val AccessInfo(accessed, mutated, refdDefns)= ctx.getAccesses(d.sym) val includedCaptures = ctx.prevFnLocals.reqCapture @@ -344,15 +340,13 @@ class Lifter(using State, Raise): .map(sym => ctx.lookup(sym).get) .toList.sortBy(_.uid) - val includedLocals = (accessed -- ctx.prevFnLocals.reqCapture).toList.sortBy(_.uid) + val refMod = refdDefns.intersect(ctx.modules) + val includedLocals = ((accessed -- ctx.prevFnLocals.reqCapture) ++ refMod).toList.sortBy(_.uid) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) val refBms = refdDefns.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) - val isMod = d match - case c: ClsLikeDefn => c.k is syntax.Mod - case _ => false - - if includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty then + if ctx.ignored(d.sym) || + (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then d match case f: FunDefn => createLiftInfoFn(f, parentCls, ctx) @@ -360,15 +354,20 @@ class Lifter(using State, Raise): createLiftInfoCls(c, ctx) case _ => Map.empty else + val modLocal = d match + case c: ClsLikeDefn if c.k is syntax.Mod => + S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) + case _ => N + val fakeCtorBms = d match - case c: ClsLikeDefn => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) + case c: ClsLikeDefn if !modLocal.isDefined => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) case _ => N val singleCallBms = BlockMemberSymbol(d.sym.nme + "$", Nil) val info = LiftedInfo( includedCaptures, includedLocals, clsCaptures, - refBms, fakeCtorBms, singleCallBms, isMod + refBms, fakeCtorBms, singleCallBms, modLocal ) d match @@ -401,25 +400,26 @@ class Lifter(using State, Raise): override def applyResult(r: Result): Result = r match // if possible, directly rewrite the call using the efficient version case c @ Call(Value.Ref(l: BlockMemberSymbol), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) => + case Some(info) if !ctx.isModule(l) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyArg(_)) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) - case None => super.applyResult(r) + case _ => super.applyResult(r) case c @ Instantiate(Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) => + case Some(info) if !ctx.isModule(l) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyPath(_)).map(_.asArg) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) - case None => super.applyResult(r) + case _ => super.applyResult(r) // if possible, directly create the bms and replace the result with it - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => createCall(l, ctx) + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => + createCall(l, ctx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) => + case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => val newSym = syms.get(l) match case None => val newSym = FlowSymbol(l.nme + "$this") @@ -456,12 +456,18 @@ class Lifter(using State, Raise): case None => ctx.getLocalPath(lhs) match case None => super.applyBlock(b) case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + + case Define(d: Defn, rest: Block) => ctx.getBmsReqdInfo(d.sym) match + case Some(LiftedInfo(modLocal = S(sym))) => + blockBuilder + .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) + .rest(applyBlock(rest)) + case _ => super.applyBlock(b) + + case _ => super.applyBlock(b) override def applyPath(p: Path): Path = p match - case Value.Ref(b: BlockMemberSymbol) => ctx.getIgnoredBmsPath(b) match - case Some(value) => Value.Ref(value) - case _ => super.applyPath(p) case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match case Some(value) if !belongsToCtor(value) => Value.Ref(value) case _ => super.applyPath(p) @@ -509,7 +515,7 @@ class Lifter(using State, Raise): case f: FunDefn => liftDefnsInFn(f, ctx) case c: ClsLikeDefn => liftDefnsInCls(c, ctx) case _ => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms, isMod)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms, modLocal)) => val createSym = d match case d: ClsLikeDefn => // due to the possibility of capturing a TempSymbol in HandlerLowering, it is necessary to generate a discriminator @@ -592,81 +598,97 @@ class Lifter(using State, Raise): Lifted(mainDefn, auxDefn :: extras) - case c: ClsLikeDefn => + case c: ClsLikeDefn if !(c.k is syntax.Mod) => val newDef = c.copy( owner = N, auxParams = c.auxParams.appended(PlainParamList(extraParams)) ) val Lifted(lifted, extras) = liftDefnsInCls(newDef, newCtx) - fakeCtorBms match - case None => Lifted(lifted, extras) // unreachable - case Some(bms) => - // create the fake ctor here + val bms = fakeCtorBms.get - inline def mapParams(ps: ParamList) = ps.params.map(p => VarSymbol(p.sym.id)) + // create the fake ctor here + inline def mapParams(ps: ParamList) = ps.params.map(p => VarSymbol(p.sym.id)) - val paramSyms = c.paramsOpt.map(mapParams) - val auxSyms = c.auxParams.map(mapParams) - val extraSyms = extraParams.map(p => VarSymbol(p.sym.id)) + val paramSyms = c.paramsOpt.map(mapParams) + val auxSyms = c.auxParams.map(mapParams) + val extraSyms = extraParams.map(p => VarSymbol(p.sym.id)) - val paramArgs = paramSyms.getOrElse(Nil).map(_.asPath) + val paramArgs = paramSyms.getOrElse(Nil).map(_.asPath) + + inline def toPaths(l: List[Local]) = l.map(_.asPath) + + var curSym = TempSymbol(None, "tmp") + val inst = Instantiate(c.sym.asPath, paramArgs) + var acc = blk => Assign(curSym, inst, blk) + for ps <- auxSyms do + val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false) + curSym = TempSymbol(None, "tmp") + acc = blk => acc(Assign(curSym, call, blk)) + val bod = acc(Return(Call(curSym.asPath, extraSyms.map(_.asPath.asArg))(true, false), false)) + + inline def toPlist(ls: List[VarSymbol]) = PlainParamList(ls.map(s => Param(FldFlags.empty, s, N))) + + val paramPlist = paramSyms.map(toPlist) + val auxPlist = auxSyms.map(toPlist) + val extraPlist = toPlist(extraSyms) + + 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 = paramPlist match + case None => extraPlist + case Some(value) => ParamList(value.flags, extraPlist.params ++ value.params, value.restParam) + + val auxCtorDefn_ = FunDefn(None, singleCallBms, headParams :: auxPlist, bod) + val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) + + Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) + case c: ClsLikeDefn => // module + // force it to be a class + val newDef = c.copy( + k = syntax.Cls, paramsOpt = N, + owner = N, auxParams = PlainParamList(extraParams) :: Nil + ) + liftDefnsInCls(newDef, newCtx) - inline def toPaths(l: List[Local]) = l.map(_.asPath) - - var curSym = TempSymbol(None, "tmp") - val inst = Instantiate(c.sym.asPath, paramArgs) - var acc = blk => Assign(curSym, inst, blk) - for ps <- auxSyms do - val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false) - curSym = TempSymbol(None, "tmp") - acc = blk => acc(Assign(curSym, call, blk)) - val bod = acc(Return(Call(curSym.asPath, extraSyms.map(_.asPath.asArg))(true, false), false)) - - inline def toPlist(ls: List[VarSymbol]) = PlainParamList(ls.map(s => Param(FldFlags.empty, s, N))) - - val paramPlist = paramSyms.map(toPlist) - val auxPlist = auxSyms.map(toPlist) - val extraPlist = toPlist(extraSyms) - - 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 = paramPlist match - case None => extraPlist - case Some(value) => ParamList(value.flags, extraPlist.params ++ value.params, value.restParam) - - val auxCtorDefn_ = FunDefn(None, singleCallBms, headParams :: auxPlist, bod) - val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) - - Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctx) val (ctor, ctorDefns) = c.ctor.floatOut(ctx) + + val allCtorDefns = preCtorDefns ++ ctorDefns + val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) + + val modPaths: Map[Local, Local] = ctorIncluded.map: + case c: ClsLikeDefn if c.k is syntax.Mod => ctx.getBmsReqdInfo(c.sym) match + case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) + case _ => S(c.sym -> c.sym) + case _ => None + .collect: + case Some(x) => x + .toMap - val newCtx = ctx.addIsymPath(c.isym, c.isym) - // TODO: add block member symbol replacement + val newCtx = ctx + .addIsymPath(c.isym, c.isym) + .addLocalPaths(modPaths) val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) val newCtor = rewriteBlk(ctor, S(c), newCtx) - - val allCtorDefns = preCtorDefns ++ ctorDefns - - val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) + val ctorDefnsLifted = ctorIncluded.flatMap: defn => val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) @@ -698,25 +720,33 @@ class Lifter(using State, Raise): val (ignored, included) = nested.partition(d => ctx.ignored(d.sym)) + val modPaths: Map[Local, Local] = nested.map: + case c: ClsLikeDefn if c.k is syntax.Mod => ctx.getBmsReqdInfo(c.sym) match + case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) + case _ => S(c.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 = FlowSymbol("capture") val captureCtx = ctx .addLocalCaptureSyms(varsMap) // how to access locals via. the capture class from now on .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture + .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> s).toMap) + .addLocalPaths(modPaths) + .addIgnoredBmsPaths(ignored.map(d => d.sym -> d.sym).toMap) val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) // lift out the nested defns val nestedLifted = included.map(liftOutDefnCont(f, _, nestedCtx)) - val newDefns = nestedLifted.flatMap: + val ignoredExtra = ignored.flatMap(liftOutDefnCont(f, _, nestedCtx).extraDefns) + val newDefns = ignoredExtra ++ nestedLifted.flatMap: case Lifted(liftedDefn, extraDefns) => liftedDefn :: extraDefns - // some book-keeping - val thisVars = ctx.usedLocals(f.sym) - val newCtx = captureCtx - .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> s).toMap) - .addIgnoredBmsPaths(ignored.map(d => d.sym -> d.sym).toMap) - - val transformed = rewriteBlk(blk, N, newCtx) + val transformed = rewriteBlk(blk, N, captureCtx) if thisVars.reqCapture.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) @@ -762,7 +792,6 @@ class Lifter(using State, Raise): val blk = desugarLambdas(b) val analyzer = UsedVarAnalyzer(blk) val ctx = LifterCtx.withLocals(analyzer.findUsedLocals).withAccesses(analyzer.accessMap) - val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => @@ -789,47 +818,75 @@ class UsedVarAnalyzer(b: Block)(using State): // 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 findDefinedLocals: (Map[BlockMemberSymbol, Set[Local]], Map[BlockMemberSymbol, Defn]) = + 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 + nestedDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]], // definitions that this defn contains, including itself + ) + private def createMetadata: DefnMetadata = var defnsMap: Map[BlockMemberSymbol, Defn] = Map.empty - var usedMap: Map[BlockMemberSymbol, Set[Local]] = Map.empty + var definedLocals: Map[BlockMemberSymbol, Set[Local]] = Map.empty + var existingVars: Map[BlockMemberSymbol, Set[Local]] = Map.empty + var nestedDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty - def findDefinedLocalsFn(f: FunDefn, existing: Set[Local]): Unit = + def createMetadataFn(f: FunDefn, existing: Set[Local]): Unit = + existingVars += (f.sym -> existing) val thisVars = Lifter.getVars(f) -- existing val newExisting = existing ++ thisVars + var thisNestedDefns: Set[BlockMemberSymbol] = Set.empty + f.sym defnsMap += (f.sym -> f) - usedMap += (f.sym -> thisVars) + definedLocals += (f.sym -> thisVars) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - findDefinedLocalsDefn(defn, newExisting) + createMetadataDefn(defn, newExisting) + thisNestedDefns ++= nestedDefns(defn.sym) defn walker.applyBlock(f.body) + nestedDefns += (f.sym -> thisNestedDefns) - def findDefinedLocalsDefn(d: Defn, existing: Set[Local]): Unit = + def createMetadataDefn(d: Defn, existing: Set[Local]): Unit = d match case f: FunDefn => - findDefinedLocalsFn(f, existing) + createMetadataFn(f, existing) case c: ClsLikeDefn => - findDefinedLocalsCls(c, existing) + createMetadataCls(c, existing) case d => Map.empty - def findDefinedLocalsCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + def createMetadataCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + existingVars += (c.sym -> existing) val thisVars = Lifter.getVars(c) -- existing val newExisting = existing ++ thisVars + var thisNestedDefns: Set[BlockMemberSymbol] = Set.empty + c.sym + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = defn match + case _: ValDefn => super.applyDefn(defn) + case _ => + createMetadataDefn(defn, newExisting) + thisNestedDefns ++= nestedDefns(defn.sym) + defn + walker.applyBlock(c.preCtor) + walker.applyBlock(c.ctor) defnsMap += (c.sym -> c) - usedMap += (c.sym -> thisVars) + definedLocals += (c.sym -> thisVars) + + for f <- c.methods do + createMetadataFn(f, newExisting) + thisNestedDefns ++ nestedDefns(f.sym) - for f <- c.methods do findDefinedLocalsFn(f, newExisting) + nestedDefns += (c.sym -> thisNestedDefns) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - findDefinedLocalsDefn(defn, b.definedVars) + createMetadataDefn(defn, b.definedVars) defn walker.applyBlock(b) - (usedMap, defnsMap) + DefnMetadata(definedLocals, defnsMap, existingVars, nestedDefns) - private val (definedLocals, defnsMap) = findDefinedLocals + val DefnMetadata(definedLocals, defnsMap, existingVars, nestedDefns) = createMetadata private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -892,16 +949,13 @@ class UsedVarAnalyzer(b: Block)(using State): private def findAccesses(f: FunDefn): Map[BlockMemberSymbol, AccessInfo] = var defns: List[Defn] = Nil var definedVarsDeep: Set[Local] = definedLocals(f.sym) - var existingVarsAt: Map[BlockMemberSymbol, Set[Local]] = Map.empty val walker = new BlockTransformer(SymbolSubst()): override def applyFunDefn(f: FunDefn): FunDefn = - existingVarsAt += (f.sym -> definedVarsDeep) defns +:= f; definedVarsDeep ++= definedLocals(f.sym) super.applyFunDefn(f) override def applyDefn(defn: Defn): Defn = - existingVarsAt += (defn.sym -> definedVarsDeep) defn match case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) case _ => @@ -948,7 +1002,7 @@ class UsedVarAnalyzer(b: Block)(using State): for (id, scc) <- sccs sym <- scc - yield sym -> (sccAccessInfo(id).intersectLocals(existingVarsAt(sym))) + yield sym -> (sccAccessInfo(id).intersectLocals(existingVars(sym)).withoutBms(nestedDefns(sym))) private def findAccessesTop = var accessMap: Map[BlockMemberSymbol, AccessInfo] = 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 8e093d15a..ade8b6429 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -288,7 +288,8 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: val pss = pss_.map(setupFunction(N, _, End())._1) val paramsDoc = pss.foldLeft(doc"($ps)"): case (doc, ps) => doc"${doc}(${ps})" - val bod = braced(doc" # return new ${sym.nme}.class$paramsDoc;") + val extraBrace = if paramsOpt.isDefined then "" else "()" + val bod = braced(doc" # return new ${sym.nme}.class$extraBrace$paramsDoc;") val funBod = pss.foldRight(bod): case (psDoc, doc_) => doc"($psDoc) => $doc_" val funBodRet = if pss.isEmpty then funBod else braced(doc" # return $funBod") diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls new file mode 100644 index 000000000..eeae16850 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls @@ -0,0 +1,4 @@ +:lift +:js + + diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index c6d523a48..86665a1c7 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -13,6 +13,51 @@ fun f() = f() + f() + f() //│ = 3 +:expect 1 +:sjs +fun f(x) = + class Test() with + fun get() = x + Test() +f(1).get() +//│ JS (unsanitized): +//│ let Test1, f1, tmp1, Test$ctor, Test$; +//│ Test$ = function Test$(x$0) { +//│ let tmp2; +//│ tmp2 = new Test1(); +//│ return tmp2(x$0) +//│ }; +//│ Test$ctor = function Test$ctor(x$0) { +//│ return () => { +//│ let tmp2; +//│ tmp2 = new Test1(); +//│ return tmp2(x$0) +//│ } +//│ }; +//│ Test1 = function Test() { +//│ return (x$01) => { +//│ return new Test.class()(x$01); +//│ } +//│ }; +//│ Test1.class = class Test { +//│ constructor() { +//│ return (x$0) => { +//│ this.x$0 = x$0; +//│ return this; +//│ } +//│ } +//│ get() { +//│ return this.x$0 +//│ } +//│ toString() { return "Test(" + "" + ")"; } +//│ }; +//│ f1 = function f(x) { +//│ return Test$(x) +//│ }; +//│ tmp1 = f1(1); +//│ runtime.safeCall(tmp1.get()) +//│ = 1 + :expect 1 :sjs fun f(used1, unused1) = @@ -26,7 +71,7 @@ fun f(used1, unused1) = Test(unused1) f(1, 2).get() //│ JS (unsanitized): -//│ let h, Test1, g, f1, tmp1, Test$ctor, Test$, g$, h$; +//│ let h, Test3, g, f2, tmp2, Test$ctor1, Test$1, g$, h$; //│ h$ = function h$(used3) { //│ return used3 //│ }; @@ -36,34 +81,34 @@ f(1, 2).get() //│ } //│ }; //│ g$ = function g$(used1, g_arg) { -//│ let used3, tmp2; +//│ let used3, tmp3; //│ used3 = 2; -//│ tmp2 = h$(used3); -//│ return used1 + tmp2 +//│ tmp3 = h$(used3); +//│ return used1 + tmp3 //│ }; //│ g = function g(used1) { //│ return (g_arg) => { //│ return g$(used1, g_arg) //│ } //│ }; -//│ Test$ = function Test$(used1$0, a) { -//│ let tmp2; -//│ tmp2 = new Test1(a); -//│ return tmp2(used1$0) +//│ Test$1 = function Test$(used1$0, a) { +//│ let tmp3; +//│ tmp3 = new Test3(a); +//│ return tmp3(used1$0) //│ }; -//│ Test$ctor = function Test$ctor(used1$0) { +//│ Test$ctor1 = function Test$ctor(used1$0) { //│ return (a) => { -//│ let tmp2; -//│ tmp2 = new Test1(a); -//│ return tmp2(used1$0) +//│ let tmp3; +//│ tmp3 = new Test3(a); +//│ return tmp3(used1$0) //│ } //│ }; -//│ Test1 = function Test(a1) { +//│ Test3 = function Test(a1) { //│ return (used1$01) => { //│ return new Test.class(a1)(used1$01); //│ } //│ }; -//│ Test1.class = class Test { +//│ Test3.class = class Test2 { //│ constructor(a) { //│ return (used1$0) => { //│ this.a = a; @@ -76,13 +121,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test(" + globalThis.Predef.render(this.a) + ")"; } //│ }; -//│ f1 = function f(used1, unused1) { +//│ f2 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; -//│ return Test$(used1, unused1) +//│ return Test$1(used1, unused1) //│ }; -//│ tmp1 = f1(1, 2); -//│ runtime.safeCall(tmp1.get()) +//│ tmp2 = f2(1, 2); +//│ runtime.safeCall(tmp2.get()) //│ = 1 :todo @@ -100,7 +145,7 @@ fun f(used1, unused1) = foo(unused1) f(1, 2).get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test$ctor (class hkmc2.semantics.BlockMemberSymbol) +//│ = 1 :expect 1 fun f(used1, unused1) = @@ -124,17 +169,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f4, A$ctor, A$, f$capture3; +//│ let A1, f5, A$ctor, A$, f$capture3; //│ A$ = function A$(f$capture$0) { -//│ let tmp4; -//│ tmp4 = new A1(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1(); +//│ return tmp5(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp4; -//│ tmp4 = new A1(); -//│ return tmp4(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1(); +//│ return tmp5(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -164,14 +209,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f4 = function f(x) { -//│ let tmp4, tmp5, capture; +//│ f5 = function f(x) { +//│ let tmp5, tmp6, capture; //│ capture = new f$capture3(x); -//│ tmp4 = A$(capture); -//│ tmp5 = runtime.safeCall(tmp4.f()); +//│ tmp5 = A$(capture); +//│ tmp6 = runtime.safeCall(tmp5.f()); //│ return capture.x0$ //│ }; -//│ f4(1) +//│ f5(1) //│ = 2 // only w should be in a capture @@ -193,17 +238,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f5, tmp4, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; +//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; //│ Good$ = function Good$(x$1, y$2, f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1(); +//│ return tmp6(x$1, y$2, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Good1(); -//│ return tmp5(x$1, y$2, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1(); +//│ return tmp6(x$1, y$2, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -221,24 +266,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp5, tmp6; +//│ let tmp6, tmp7; //│ this.f$capture$0.z1$ = 100; -//│ tmp5 = this.x$1 + this.y$2; -//│ tmp6 = tmp5 + this.f$capture$0.z1$; -//│ return tmp6 + this.f$capture$0.w0$ +//│ tmp6 = this.x$1 + this.y$2; +//│ tmp7 = tmp6 + this.f$capture$0.z1$; +//│ return tmp7 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp5; -//│ tmp5 = new Bad1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1(); +//│ return tmp6(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new Bad1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1(); +//│ return tmp6(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -269,19 +314,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ", " + globalThis.Predef.render(this.z1$) + ")"; } //│ }; -//│ f5 = function f() { -//│ let x, y, tmp5, tmp6, capture; +//│ f6 = function f() { +//│ let x, y, tmp6, tmp7, capture; //│ capture = new f$capture5(null, null); //│ x = 1; //│ y = 10; //│ capture.z1$ = 10; //│ capture.w0$ = 1000; -//│ tmp5 = Bad$(capture); -//│ tmp6 = runtime.safeCall(tmp5.foo()); +//│ tmp6 = Bad$(capture); +//│ tmp7 = runtime.safeCall(tmp6.foo()); //│ return Good$(x, y, capture) //│ }; -//│ tmp4 = f5(); -//│ runtime.safeCall(tmp4.foo()) +//│ tmp5 = f6(); +//│ runtime.safeCall(tmp5.foo()) //│ = 10111 :fixme @@ -355,7 +400,63 @@ fun f() = foo()(0) f().get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ = 0 +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test (class hkmc2.semantics.BlockMemberSymbol) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:50) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:55) +//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:92) +//│ at: scala.Option.getOrElse(Option.scala:201) +//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:94) +//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) +//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) +//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:326) +//│ at: hkmc2.codegen.js.JSBuilder.block(JSBuilder.scala:458) +//│ at: hkmc2.codegen.js.JSBuilder.body(JSBuilder.scala:461) + +:sjs +:lift +:w +fun f(x) = + class Test() with + fun get() = + fun h() = x + h() + let foo = Test + foo() +f(2).get() +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ JS (unsanitized): +//│ let h3, f9, tmp12, h$3; +//│ h$3 = function h$(Test$instance, x) { +//│ return x +//│ }; +//│ h3 = function h(Test$instance, x) { +//│ return () => { +//│ return h$3(Test$instance, x) +//│ } +//│ }; +//│ f9 = function f(x) { +//│ let Test9, foo3; +//│ Test9 = function Test() { +//│ return new Test.class(); +//│ }; +//│ Test9.class = class Test8 { +//│ constructor() {} +//│ get() { +//│ h3 = function h() { +//│ return x +//│ }; +//│ return h3() +//│ } +//│ toString() { return "Test(" + "" + ")"; } +//│ }; +//│ foo3 = Test9; +//│ return runtime.safeCall(foo3()) +//│ }; +//│ tmp12 = f9(2); +//│ runtime.safeCall(tmp12.get()) +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ = 2 :fixme class Test diff --git a/hkmc2/shared/src/test/mlscript/lifter/Modules.mls b/hkmc2/shared/src/test/mlscript/lifter/Modules.mls new file mode 100644 index 000000000..e9ca3bc5b --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/Modules.mls @@ -0,0 +1,67 @@ +:lift +:js + +:fixme +:expect 12 +:sjs +fun foo(x, y) = + module M with + val test = 2 + fun foo() = + set y = 2 + x + y + test + M.foo() +foo(10, 0) +//│ JS (unsanitized): +//│ let M1, foo, foo$capture1; +//│ M1 = function M(x$11, foo$capture$01) { +//│ return new M.class()(x$11, foo$capture$01); +//│ }; +//│ M1.class = class M { +//│ constructor() { +//│ return (x$1, foo$capture$0) => { +//│ this.x$1 = x$1; +//│ this.foo$capture$0 = foo$capture$0; +//│ this.test = 2; +//│ return this; +//│ } +//│ } +//│ foo() { +//│ let tmp; +//│ M.foo$capture$0.y0$ = 2; +//│ tmp = M.x$1 + M.foo$capture$0.y0$; +//│ return tmp + M.test +//│ } +//│ toString() { return "M"; } +//│ }; +//│ foo$capture1 = function foo$capture(y0$1) { +//│ return new foo$capture.class(y0$1); +//│ }; +//│ foo$capture1.class = class foo$capture { +//│ constructor(y0$) { +//│ this.y0$ = y0$; +//│ } +//│ toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } +//│ }; +//│ foo = function foo(x, y) { +//│ let M$, capture; +//│ capture = new foo$capture1(y); +//│ M$ = M1(x, capture); +//│ return M$.foo() +//│ }; +//│ foo(10, 0) +//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' + +:fixme +:expect 12 +fun foo(x, y) = + module M with + fun foo() = + set y = 2 + x + y + fun foo = M.foo() + foo +foo(10, 0) +//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' From c831ae9b3d5d37798e410d3c10229550f55a51a7 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 02:34:13 +0800 Subject: [PATCH 066/127] most things working --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 223 ++++++++++-------- hkmc2/shared/src/test/mlscript/HkScratch.mls | 30 ++- .../src/test/mlscript/lifter/ClassInClass.mls | 117 ++++++++- .../src/test/mlscript/lifter/ClassInFun.mls | 13 +- .../src/test/mlscript/lifter/Modules.mls | 59 ++++- 5 files changed, 324 insertions(+), 118 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 793c9a951..796eb7875 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -74,6 +74,11 @@ object Lifter: mutated, refdDefns -- locals ) + def intersectBms(locals: Set[BlockMemberSymbol]) = AccessInfo( + accessed, + mutated, + refdDefns.intersect(locals) + ) def addAccess(l: Local) = this.copy(accessed = accessed + l) def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) @@ -87,6 +92,14 @@ object Lifter: case s: FlowSymbol if !(s is state.runtimeSymbol) => s case _ => Set.empty + object RefOfBms: + def unapply(p: Path) = p match + case Value.Ref(l: BlockMemberSymbol) => S(l) + case s @ Select(_, _) => s.symbol match + case Some(value: BlockMemberSymbol) => S(value) + case _ => N + case _ => N + /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. @@ -102,6 +115,7 @@ class Lifter(using State, Raise): * @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 modules The modules in the block to be lifted. * @param localCaptureSyms The symbols in a capture corresponding to a particular local * @param prevFnLocals Locals belonging to function definitions that have already been traversed @@ -113,18 +127,20 @@ class Lifter(using State, Raise): * @param iSymPaths The path to access a particular `innerSymbol` (possibly belonging to a previous class) in the current scope */ case class LifterCtx( - val usedLocals: UsedLocalsMap, - val accessInfo: Map[BlockMemberSymbol, AccessInfo], - val ignoredDefns: Set[BlockMemberSymbol], - val modules: Set[BlockMemberSymbol], - val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol], - val prevFnLocals: FreeVars, - val prevClsDefns: List[ClsLikeDefn], - val capturePaths: Map[BlockMemberSymbol, Path], - val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo], // required captures - val ignoredBmsPaths: Map[BlockMemberSymbol, Local], - val localPaths: Map[Local, Local], - val isymPaths: Map[InnerSymbol, Local], + val defns: Map[BlockMemberSymbol, 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 modules: Set[BlockMemberSymbol] = Set.empty, + val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol] = Map.empty, + val prevFnLocals: FreeVars = FreeVars.empty, + val prevClsDefns: List[ClsLikeDefn] = Nil, + val capturePaths: Map[BlockMemberSymbol, Path] = Map.empty, + val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo] = Map.empty, // required captures + val ignoredBmsPaths: Map[BlockMemberSymbol, Local] = Map.empty, + val localPaths: Map[Local, Local] = Map.empty, + val isymPaths: Map[InnerSymbol, Local] = Map.empty, ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -144,7 +160,9 @@ class Lifter(using State, Raise): def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) def addModules(mods: Set[BlockMemberSymbol]) = copy(modules = mods ++ modules) + def withDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(defns = mp) def withAccesses(mp: Map[BlockMemberSymbol, AccessInfo]) = copy(accessInfo = mp) + def withInScopes(mp: Map[BlockMemberSymbol, Set[BlockMemberSymbol]]) = copy(inScopeDefns = mp) def addFnLocals(f: FreeVars) = copy(prevFnLocals = prevFnLocals ++ f) def addClsDefn(c: ClsLikeDefn) = copy(prevClsDefns = c :: prevClsDefns) def addLocalCaptureSyms(m: Map[Local, LocalSymbol & NamedSymbol]) = copy(localCaptureSyms = localCaptureSyms ++ m) @@ -161,8 +179,7 @@ class Lifter(using State, Raise): def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) object LifterCtx: - def empty = LifterCtx(UsedLocalsMap(Map.empty), Map.empty, Set.empty, Set.empty, - Map.empty, FreeVars.empty, Nil, Map.empty, Map.empty, Map.empty, Map.empty, Map.empty) + def empty = LifterCtx() def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) /** @@ -264,7 +281,7 @@ class Lifter(using State, Raise): val reqdBms: List[BlockMemberSymbol], // pass ignored blockmembersymbols val fakeCtorBms: Option[BlockMemberSymbol], // only for classes val singleCallBms: BlockMemberSymbol, // optimization - val modLocal: Opt[FlowSymbol] // for modules + val modLocal: Opt[Local] // for modules ) case class Lifted[+T <: Defn]( @@ -315,7 +332,7 @@ class Lifter(using State, Raise): case _ => super.applyResult(r) override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) if clsSyms.contains(l) && !modules.contains(l) => + case RefOfBms(l) if clsSyms.contains(l) && !l.isModule => raise(WarningReport( msg"Cannot yet lift the class `${l.nme}` as it is used as a higher-order class." -> N :: Nil, N, Diagnostic.Source.Compilation @@ -333,30 +350,33 @@ class Lifter(using State, Raise): def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val AccessInfo(accessed, mutated, refdDefns)= ctx.getAccesses(d.sym) + val AccessInfo(accessed, mutated, 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 = refdDefns.intersect(ctx.modules) + val refMod = inScopeRefs.intersect(ctx.modules) val includedLocals = ((accessed -- ctx.prevFnLocals.reqCapture) ++ refMod).toList.sortBy(_.uid) val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) - val refBms = refdDefns.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) + val refBms = inScopeRefs.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) if ctx.ignored(d.sym) || (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then d match case f: FunDefn => - createLiftInfoFn(f, parentCls, ctx) + createLiftInfoFn(f, ctx) case c: ClsLikeDefn => createLiftInfoCls(c, ctx) case _ => Map.empty else val modLocal = d match - case c: ClsLikeDefn if c.k is syntax.Mod => - S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) + case c: ClsLikeDefn if c.k is syntax.Mod => parentCls match + case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) + case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) case _ => N val fakeCtorBms = d match @@ -372,20 +392,20 @@ class Lifter(using State, Raise): d match case f: FunDefn => - createLiftInfoFn(f, parentCls, ctx) + (d.sym -> info) + createLiftInfoFn(f, ctx) + (d.sym -> info) case c: ClsLikeDefn => createLiftInfoCls(c, ctx) + (d.sym -> info) case _ => Map.empty - def createLiftInfoFn(f: FunDefn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = + def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val (_, defns) = f.body.floatOut(ctx) defns.flatMap(createLiftInfoCont(_, N, ctx.addFnLocals(ctx.usedLocals(f.sym)))).toMap def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = c.preCtor.floatOut(ctx)._2 ++ c.ctor.floatOut(ctx)._2 val newCtx = ctx.addClsDefn(c) - defns.flatMap(f => createLiftInfoCont(f, N, newCtx)).toMap - ++ c.methods.flatMap(f => createLiftInfoFn(f, S(c), newCtx)) + defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap + ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and @@ -399,13 +419,13 @@ class Lifter(using State, Raise): override def applyResult(r: Result): Result = r match // if possible, directly rewrite the call using the efficient version - case c @ Call(Value.Ref(l: BlockMemberSymbol), args) => ctx.bmsReqdInfo.get(l) match + case c @ Call(RefOfBms(l), args) => ctx.bmsReqdInfo.get(l) match case Some(info) if !ctx.isModule(l) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyArg(_)) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) case _ => super.applyResult(r) - case c @ Instantiate(Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")), args) => + case c @ Instantiate(Select(RefOfBms(l), Tree.Ident("class")), args) => ctx.bmsReqdInfo.get(l) match case Some(info) if !ctx.isModule(l) => val extraArgs = getCallArgs(l, ctx) @@ -413,13 +433,13 @@ class Lifter(using State, Raise): Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) case _ => super.applyResult(r) // if possible, directly create the bms and replace the result with it - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => createCall(l, ctx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier - override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => + override def applyPath(p: Path): Path = p match + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => val newSym = syms.get(l) match case None => val newSym = FlowSymbol(l.nme + "$this") @@ -427,7 +447,11 @@ class Lifter(using State, Raise): newSym case Some(value) => value Value.Ref(newSym) - case _ => super.applyValue(v) + case RefOfBms(l) => ctx.getIgnoredBmsPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + + case _ => super.applyPath(p) (walker.applyBlock(b), syms.toList) end rewriteBms @@ -475,6 +499,9 @@ class Lifter(using State, Raise): ctx.getIsymPath(t.owner.get) match case Some(value) if !belongsToCtor(value) => Select(value.asPath, t.id)(N) case _ => super.applyPath(p) + case s @ Select(qual, ident) => s.symbol.flatMap(ctx.getLocalPath) match + case Some(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) + case _ => super.applyPath(p) case Value.Ref(l) => ctx.getLocalCaptureSym(l) match case Some(captureSym) => Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) @@ -489,7 +516,10 @@ class Lifter(using State, Raise): val (rewriten, syms) = rewriteBms(b, ctx) val pre = syms.foldLeft(blockBuilder): case (blk, (bms, local)) => - blk.assign(local, createCall(bms, ctx)) + val initial = blk.assign(local, createCall(bms, ctx)) + ctx.defns(bms) match + case c: ClsLikeDefn => initial.assignFieldN(local.asPath, Tree.Ident("class"), bms.asPath) + case _ => initial pre.rest(super.applyBlock(rewriten)) b |> transformer1.applyBlock |> transformer2.applyBlock @@ -791,14 +821,18 @@ class Lifter(using State, Raise): def transform(b: Block) = val blk = desugarLambdas(b) val analyzer = UsedVarAnalyzer(blk) - val ctx = LifterCtx.withLocals(analyzer.findUsedLocals).withAccesses(analyzer.accessMap) + val ctx = LifterCtx + .withLocals(analyzer.findUsedLocals) + .withDefns(analyzer.defnsMap) + .withAccesses(analyzer.accessMap) + .withInScopes(analyzer.inScopeDefns) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => val (unliftable, modules) = createMetadata(d) val ctxx = ctx.addIgnored(unliftable).addModules(modules) val Lifted(lifted, extra) = d match - case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, None, ctxx))) + case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, ctxx))) case c: ClsLikeDefn => liftDefnsInCls(c, ctxx.addBmsReqdInfo(createLiftInfoCls(c, ctxx))) case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) @@ -822,71 +856,76 @@ class UsedVarAnalyzer(b: Block)(using State): 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 - nestedDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]], // definitions that this defn contains, including itself + inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]], // definitions that are in scope + nestedDefns: Map[BlockMemberSymbol, List[Defn]], // definitions directly nested within another defn (shallow) ) 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 nestedDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty + var inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty + var nestedDefns: Map[BlockMemberSymbol, List[Defn]] = Map.empty - def createMetadataFn(f: FunDefn, existing: Set[Local]): Unit = + def createMetadataFn(f: FunDefn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = existingVars += (f.sym -> existing) val thisVars = Lifter.getVars(f) -- existing val newExisting = existing ++ thisVars - var thisNestedDefns: Set[BlockMemberSymbol] = Set.empty + f.sym + + val thisScopeDefns: List[Defn] = f.body.floatOutDefns()._2 + + nestedDefns += f.sym -> thisScopeDefns + + val newInScope = inScope ++ thisScopeDefns.map(_.sym) + for s <- thisScopeDefns do + inScopeDefns += s.sym -> (newInScope - s.sym) defnsMap += (f.sym -> f) definedLocals += (f.sym -> thisVars) + + for d <- thisScopeDefns do createMetadataDefn(d, newExisting, newInScope) + val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - createMetadataDefn(defn, newExisting) - thisNestedDefns ++= nestedDefns(defn.sym) + createMetadataDefn(defn, newExisting, inScope) defn walker.applyBlock(f.body) - nestedDefns += (f.sym -> thisNestedDefns) - def createMetadataDefn(d: Defn, existing: Set[Local]): Unit = + def createMetadataDefn(d: Defn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = d match case f: FunDefn => - createMetadataFn(f, existing) + createMetadataFn(f, existing, inScope) case c: ClsLikeDefn => - createMetadataCls(c, existing) + createMetadataCls(c, existing, inScope) case d => Map.empty - def createMetadataCls(c: ClsLikeDefn, existing: Set[Local]): Unit = + def createMetadataCls(c: ClsLikeDefn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = existingVars += (c.sym -> existing) val thisVars = Lifter.getVars(c) -- existing val newExisting = existing ++ thisVars - var thisNestedDefns: Set[BlockMemberSymbol] = Set.empty + c.sym - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = defn match - case _: ValDefn => super.applyDefn(defn) - case _ => - createMetadataDefn(defn, newExisting) - thisNestedDefns ++= nestedDefns(defn.sym) - defn - walker.applyBlock(c.preCtor) - walker.applyBlock(c.ctor) + val thisScopeDefns: List[Defn] = + (c.methods ++ c.preCtor.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2) + nestedDefns += c.sym -> thisScopeDefns + + val newInScope = inScope ++ thisScopeDefns.map(_.sym) + for s <- thisScopeDefns do + inScopeDefns += s.sym -> (newInScope - s.sym) + defnsMap += (c.sym -> c) definedLocals += (c.sym -> thisVars) - for f <- c.methods do - createMetadataFn(f, newExisting) - thisNestedDefns ++ nestedDefns(f.sym) - - nestedDefns += (c.sym -> thisNestedDefns) + for d <- thisScopeDefns do createMetadataDefn(d, newExisting, newInScope) val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = - createMetadataDefn(defn, b.definedVars) + override def applyDefn(defn: Defn): Defn = + inScopeDefns += defn.sym -> Set.empty + createMetadataDefn(defn, b.definedVars, Set.empty) defn walker.applyBlock(b) - DefnMetadata(definedLocals, defnsMap, existingVars, nestedDefns) + DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns) - val DefnMetadata(definedLocals, defnsMap, existingVars, nestedDefns) = createMetadata + val DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns) = createMetadata private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -907,7 +946,7 @@ class UsedVarAnalyzer(b: Block)(using State): override def applyValue(v: Value): Value = v match case Value.Ref(_: BuiltinSymbol) => super.applyValue(v) - case Value.Ref(l: BlockMemberSymbol) => + case RefOfBms(l) => accessed = accessed.addRefdDefn(l); v case Value.Ref(l) => accessed = accessed.addAccess(l); v @@ -946,9 +985,9 @@ class UsedVarAnalyzer(b: Block)(using State): ret // MUST be called from a top-level defn - private def findAccesses(f: FunDefn): Map[BlockMemberSymbol, AccessInfo] = + private def findAccesses(d: Defn): Map[BlockMemberSymbol, AccessInfo] = var defns: List[Defn] = Nil - var definedVarsDeep: Set[Local] = definedLocals(f.sym) + var definedVarsDeep: Set[Local] = Set.empty val walker = new BlockTransformer(SymbolSubst()): override def applyFunDefn(f: FunDefn): FunDefn = @@ -960,7 +999,8 @@ class UsedVarAnalyzer(b: Block)(using State): case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) case _ => super.applyDefn(defn) - walker.applyBlock(f.body) + + walker.applyDefn(d) val defnSyms = defns.map(_.sym).toSet val accessInfo = defns.map: d => @@ -1002,16 +1042,14 @@ class UsedVarAnalyzer(b: Block)(using State): for (id, scc) <- sccs sym <- scc - yield sym -> (sccAccessInfo(id).intersectLocals(existingVars(sym)).withoutBms(nestedDefns(sym))) + yield sym -> (sccAccessInfo(id).intersectLocals(existingVars(sym))) private def findAccessesTop = var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = defn match - case f: FunDefn => - accessMap ++= findAccesses(f); f - case c: ClsLikeDefn => - for f <- c.methods do accessMap ++= findAccesses(f); c + case _: FunDefn | _: ClsLikeDefn => + accessMap ++= findAccesses(defn); defn case _ => super.applyDefn(defn) walker.applyBlock(b) accessMap @@ -1118,25 +1156,25 @@ class UsedVarAnalyzer(b: Block)(using State): hasMutator += l override def applyResult(r: Result): Result = r match - case Call(Value.Ref(l: BlockMemberSymbol), args) => + case Call(RefOfBms(l), args) => args.map(super.applyArg(_)) handleCalledBms(l) r - case Instantiate(Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")), args) => + case Instantiate(Select(RefOfBms(l), Tree.Ident("class")), args) => args.map(super.applyPath(_)) handleCalledBms(l) r case _ => super.applyResult(r) - override def applyValue(v: Value): Value = v match - case Value.Ref(l: BlockMemberSymbol) => + override def applyPath(p: Path): Path = p match + case RefOfBms(l) => defnSyms.get(l) match - case None => super.applyValue(v) + case None => super.applyPath(p) case Some(defn) => val isMod = defn match case c: ClsLikeDefn => c.k is syntax.Mod case _ => false - if isMod then super.applyValue(v) + if isMod then super.applyPath(p) else val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) val muts = muted.intersect(thisVars) @@ -1159,11 +1197,11 @@ class UsedVarAnalyzer(b: Block)(using State): reqCapture += l hasMutator += l - v + p case Value.Ref(l) => if hasMutator.contains(l) then reqCapture += (l) - v - case _ => super.applyValue(v) + p + case _ => super.applyPath(p) override def applyDefn(defn: Defn): Defn = defn match case c: ClsLikeDefn if c.k is syntax.Mod => @@ -1188,17 +1226,8 @@ class UsedVarAnalyzer(b: Block)(using State): var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty usedMap += (f.sym -> Lifter.FreeVars(vars.intersect(thisVars), cap.intersect(thisVars))) - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = defn match - case f: FunDefn => - usedMap ++= findUsedLocalsFn(f) - f - case c: ClsLikeDefn => - for f <- c.methods do - usedMap ++= findUsedLocalsFn(f) - c - case d => super.applyDefn(d) - walker.applyBlock(f.body) + for d <- nestedDefns(f.sym) do + usedMap ++= findUsedLocalsDefn(d) usedMap private def findUsedLocalsDefn(d: Defn) = @@ -1210,8 +1239,8 @@ class UsedVarAnalyzer(b: Block)(using State): case d => Map.empty private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = - c.methods.foldLeft(Map.empty): - case (acc, f) => acc ++ findUsedLocalsFn(f) + nestedDefns(c.sym).foldLeft(Map.empty): + case (acc, d) => acc ++ findUsedLocalsDefn(d) /** * Finds the used locals of functions which have been used by their nested definitions. @@ -1222,7 +1251,7 @@ class UsedVarAnalyzer(b: Block)(using State): def findUsedLocals: Lifter.UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = + override def applyDefn(defn: Defn): Defn = usedMap ++= findUsedLocalsDefn(defn) defn diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..7cc31d582 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,5 +7,31 @@ :global :d - - +:sjs +class A(x) with + module M with + fun getB() = x + fun getA() = M.getB() +//│ Elab: { Cls AParamList(‹›,List(Param(‹›,class:A‹374›.x,None)),None) { ‹› fun member:getA‹371›() = (class:A‹374›#666.)M‹member:M‹372››.getB‹member:getB‹370››(); Mod M { ‹module› fun member:getB‹370›() = class:A‹374›.x#666; }; }; } +//│ JS (unsanitized): +//│ let A1; +//│ A1 = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ A1.class = class A { +//│ constructor(x) { +//│ this.x = x; +//│ const this$A = this; +//│ this.M = class M { +//│ static {} +//│ static getB() { +//│ return this$A.x +//│ } +//│ static toString() { return "M"; } +//│ }; +//│ } +//│ getA() { +//│ return this.M.getB() +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls index eeae16850..ebcd550a0 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls @@ -1,4 +1,119 @@ -:lift :js +:lift + +:sjs +class A(x) with + class B(y) with + fun getB() = x + y + fun getA() = B(2).getB() +//│ JS (unsanitized): +//│ let B1, A1, B$ctor, B$; +//│ B$ = function B$(A$instance$0, y) { +//│ let tmp; +//│ tmp = new B1(y); +//│ return tmp(A$instance$0) +//│ }; +//│ B$ctor = function B$ctor(A$instance$0) { +//│ return (y) => { +//│ let tmp; +//│ tmp = new B1(y); +//│ return tmp(A$instance$0) +//│ } +//│ }; +//│ B1 = function B(y1) { +//│ return (A$instance$01) => { +//│ return new B.class(y1)(A$instance$01); +//│ } +//│ }; +//│ B1.class = class B { +//│ constructor(y) { +//│ return (A$instance$0) => { +//│ this.y = y; +//│ this.A$instance$0 = A$instance$0; +//│ return this; +//│ } +//│ } +//│ getB() { +//│ return this.A$instance$0.x + this.y +//│ } +//│ toString() { return "B(" + globalThis.Predef.render(this.y) + ")"; } +//│ }; +//│ A1 = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ A1.class = class A { +//│ constructor(x) { +//│ this.x = x; +//│ } +//│ getA() { +//│ let tmp; +//│ tmp = B$(this, 2); +//│ return runtime.safeCall(tmp.getB()) +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; + +:expect 3 +A(1).getA() +//│ = 3 +:sjs +class A(x) with + class B(y) with + fun getB() = x + y + fun getA() = this.B(2).getB() +//│ JS (unsanitized): +//│ let B3, A3, B$ctor1, B$1; +//│ B$1 = function B$(A$instance$0, y) { +//│ let tmp1; +//│ tmp1 = new B3(y); +//│ return tmp1(A$instance$0) +//│ }; +//│ B$ctor1 = function B$ctor(A$instance$0) { +//│ return (y) => { +//│ let tmp1; +//│ tmp1 = new B3(y); +//│ return tmp1(A$instance$0) +//│ } +//│ }; +//│ B3 = function B(y1) { +//│ return (A$instance$01) => { +//│ return new B.class(y1)(A$instance$01); +//│ } +//│ }; +//│ B3.class = class B2 { +//│ constructor(y) { +//│ return (A$instance$0) => { +//│ this.y = y; +//│ this.A$instance$0 = A$instance$0; +//│ return this; +//│ } +//│ } +//│ getB() { +//│ return this.A$instance$0.x + this.y +//│ } +//│ toString() { return "B(" + globalThis.Predef.render(this.y) + ")"; } +//│ }; +//│ A3 = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ A3.class = class A2 { +//│ constructor(x) { +//│ this.x = x; +//│ } +//│ getA() { +//│ let tmp1; +//│ tmp1 = runtime.safeCall(this.B(2)); +//│ return runtime.safeCall(tmp1.getB()) +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; +// I use the optional `symbol` parameter of `Select` to detect references to the +// BlockMemberSymbol. But when this symbol is not present, there is no way to properly +// detect it. What do to? +:fixme +:expect 3 +A(1).getA() +//│ ═══[RUNTIME ERROR] TypeError: this.B is not a function +//│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 86665a1c7..43db8b2bb 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -400,18 +400,7 @@ fun f() = foo()(0) f().get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test (class hkmc2.semantics.BlockMemberSymbol) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:50) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:55) -//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:92) -//│ at: scala.Option.getOrElse(Option.scala:201) -//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:94) -//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) -//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) -//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:326) -//│ at: hkmc2.codegen.js.JSBuilder.block(JSBuilder.scala:458) -//│ at: hkmc2.codegen.js.JSBuilder.body(JSBuilder.scala:461) +//│ = 0 :sjs :lift diff --git a/hkmc2/shared/src/test/mlscript/lifter/Modules.mls b/hkmc2/shared/src/test/mlscript/lifter/Modules.mls index e9ca3bc5b..acf4ac226 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Modules.mls @@ -1,8 +1,9 @@ :lift :js -:fixme -:expect 12 +// the problem with all of these tests is that a Ref to a module's own ModuleSymbol +// (let's say M) emits `M` instead of `this` in the codegen + :sjs fun foo(x, y) = module M with @@ -11,7 +12,6 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() -foo(10, 0) //│ JS (unsanitized): //│ let M1, foo, foo$capture1; //│ M1 = function M(x$11, foo$capture$01) { @@ -49,12 +49,13 @@ foo(10, 0) //│ M$ = M1(x, capture); //│ return M$.foo() //│ }; -//│ foo(10, 0) -//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' :fixme :expect 12 +foo(10, 0) +//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' + fun foo(x, y) = module M with fun foo() = @@ -62,6 +63,52 @@ fun foo(x, y) = x + y fun foo = M.foo() foo + +:fixme +:expect 12 foo(10, 0) //│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' + + +:sjs +class A(x) with + module M with + fun getB() = x + fun getA() = M.getB() +//│ JS (unsanitized): +//│ let M5, A1; +//│ M5 = function M(A$instance$01) { +//│ return new M.class()(A$instance$01); +//│ }; +//│ M5.class = class M4 { +//│ constructor() { +//│ return (A$instance$0) => { +//│ this.A$instance$0 = A$instance$0; +//│ return this; +//│ } +//│ } +//│ getB() { +//│ return M4.A$instance$0.x +//│ } +//│ toString() { return "M"; } +//│ }; +//│ A1 = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ A1.class = class A { +//│ constructor(x) { +//│ this.x = x; +//│ this.M$ = M5(this); +//│ } +//│ getA() { +//│ return this.M$.getB() +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; + +:fixme +:expect 2 +A(2).getA() +//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' From e4f5487bb08186cd41ae571c223c20fa87fe820a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 02:35:43 +0800 Subject: [PATCH 067/127] update test --- .../src/test/mlscript/lifter/ClassInFun.mls | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index c7bc38d6e..1e78dfd06 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -173,7 +173,7 @@ fun f(used1, unused1) = new Test f(1, 2).get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:Test$ctor (class hkmc2.semantics.BlockMemberSymbol) +//│ = 1 :sjs :expect 2 @@ -184,17 +184,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f5, A$ctor, A$, f$capture3; +//│ let A1, f6, A$ctor, A$, f$capture3; //│ A$ = function A$(f$capture$0) { -//│ let tmp5; -//│ tmp5 = new A1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new A1(); +//│ return tmp6(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp5; -//│ tmp5 = new A1(); -//│ return tmp5(f$capture$0) +//│ let tmp6; +//│ tmp6 = new A1(); +//│ return tmp6(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -224,14 +224,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f5 = function f(x) { -//│ let tmp5, tmp6, capture; +//│ f6 = function f(x) { +//│ let tmp6, tmp7, capture; //│ capture = new f$capture3(x); -//│ tmp5 = A$(capture); -//│ tmp6 = runtime.safeCall(tmp5.f()); +//│ tmp6 = A$(capture); +//│ tmp7 = runtime.safeCall(tmp6.f()); //│ return capture.x0$ //│ }; -//│ f5(1) +//│ f6(1) //│ = 2 // only w should be in a capture @@ -253,17 +253,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; +//│ let Bad1, Good1, f7, tmp6, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; //│ Good$ = function Good$(x$1, y$2, f$capture$0) { -//│ let tmp6; -//│ tmp6 = new Good1(); -//│ return tmp6(x$1, y$2, f$capture$0) +//│ let tmp7; +//│ tmp7 = new Good1(); +//│ return tmp7(x$1, y$2, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new Good1(); -//│ return tmp6(x$1, y$2, f$capture$0) +//│ let tmp7; +//│ tmp7 = new Good1(); +//│ return tmp7(x$1, y$2, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -281,24 +281,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp6, tmp7; +//│ let tmp7, tmp8; //│ this.f$capture$0.z1$ = 100; -//│ tmp6 = this.x$1 + this.y$2; -//│ tmp7 = tmp6 + this.f$capture$0.z1$; -//│ return tmp7 + this.f$capture$0.w0$ +//│ tmp7 = this.x$1 + this.y$2; +//│ tmp8 = tmp7 + this.f$capture$0.z1$; +//│ return tmp8 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp6; -//│ tmp6 = new Bad1(); -//│ return tmp6(f$capture$0) +//│ let tmp7; +//│ tmp7 = new Bad1(); +//│ return tmp7(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new Bad1(); -//│ return tmp6(f$capture$0) +//│ let tmp7; +//│ tmp7 = new Bad1(); +//│ return tmp7(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -329,19 +329,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ", " + globalThis.Predef.render(this.z1$) + ")"; } //│ }; -//│ f6 = function f() { -//│ let x, y, tmp6, tmp7, capture; +//│ f7 = function f() { +//│ let x, y, tmp7, tmp8, capture; //│ capture = new f$capture5(null, null); //│ x = 1; //│ y = 10; //│ capture.z1$ = 10; //│ capture.w0$ = 1000; -//│ tmp6 = Bad$(capture); -//│ tmp7 = runtime.safeCall(tmp6.foo()); +//│ tmp7 = Bad$(capture); +//│ tmp8 = runtime.safeCall(tmp7.foo()); //│ return Good$(x, y, capture) //│ }; -//│ tmp5 = f6(); -//│ runtime.safeCall(tmp5.foo()) +//│ tmp6 = f7(); +//│ runtime.safeCall(tmp6.foo()) //│ = 10111 :fixme @@ -430,35 +430,35 @@ fun f(x) = f(2).get() //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. //│ JS (unsanitized): -//│ let h3, f9, tmp12, h$3; -//│ h$3 = function h$(Test$instance, x) { +//│ let h4, f10, tmp13, h$4; +//│ h$4 = function h$(Test$instance, x) { //│ return x //│ }; -//│ h3 = function h(Test$instance, x) { +//│ h4 = function h(Test$instance, x) { //│ return () => { -//│ return h$3(Test$instance, x) +//│ return h$4(Test$instance, x) //│ } //│ }; -//│ f9 = function f(x) { -//│ let Test9, foo3; -//│ Test9 = function Test() { +//│ f10 = function f(x) { +//│ let Test10, foo3; +//│ Test10 = function Test() { //│ return new Test.class(); //│ }; -//│ Test9.class = class Test8 { +//│ Test10.class = class Test9 { //│ constructor() {} //│ get() { -//│ h3 = function h() { +//│ h4 = function h() { //│ return x //│ }; -//│ return h3() +//│ return h4() //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; -//│ foo3 = Test9; +//│ foo3 = Test10; //│ return runtime.safeCall(foo3()) //│ }; -//│ tmp12 = f9(2); -//│ runtime.safeCall(tmp12.get()) +//│ tmp13 = f10(2); +//│ runtime.safeCall(tmp13.get()) //│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. //│ = 2 From 2509dfebf939e4459c82f9177a6968f77a3bef4a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 13:31:50 +0800 Subject: [PATCH 068/127] Lift objects, add warning for modules, fix instantiating parameter-less classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 75 ++++++---- hkmc2/shared/src/test/mlscript/HkScratch.mls | 30 +--- .../src/test/mlscript/lifter/ClassInClass.mls | 8 +- .../src/test/mlscript/lifter/ClassInFun.mls | 96 ++++++++++--- .../src/test/mlscript/lifter/Modules.mls | 114 ---------------- .../test/mlscript/lifter/ModulesObjects.mls | 129 ++++++++++++++++++ .../test/mlscript/lifter/StackSafetyLift.mls | 32 ++--- 7 files changed, 276 insertions(+), 208 deletions(-) delete mode 100644 hkmc2/shared/src/test/mlscript/lifter/Modules.mls create mode 100644 hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 796eb7875..0dc387dcb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -15,6 +15,10 @@ import scala.collection.mutable.LinkedHashSet import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet +import hkmc2.syntax.Cls +import hkmc2.syntax.Mod +import hkmc2.syntax.Obj +import hkmc2.syntax.Pat // TODO: modules not working @@ -99,7 +103,16 @@ object Lifter: case Some(value: BlockMemberSymbol) => S(value) case _ => N case _ => N + + object InstSel: + def unapply(p: Path) = p match + case Value.Ref(l: BlockMemberSymbol) => S(l) + case s @ Select(Value.Ref(l: BlockMemberSymbol), Tree.Ident("class")) => S(l) + case _ => N + def modOrObj(d: Defn) = d match + case c: ClsLikeDefn => (c.k is syntax.Mod) || (c.k is syntax.Obj) + case _ => false /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. @@ -155,7 +168,7 @@ class Lifter(using State, Raise): def getIsymPath(l: InnerSymbol) = isymPaths.get(l) def getIgnoredBmsPath(b: BlockMemberSymbol) = ignoredBmsPaths.get(b) def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) - def isModule(b: BlockMemberSymbol) = modules.contains(b) + def isModOrObj(b: BlockMemberSymbol) = modules.contains(b) def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) @@ -291,7 +304,7 @@ class Lifter(using State, Raise): // d is a top-level definition // returns (unliftable classes, modules) - def createMetadata(d: Defn): (Set[BlockMemberSymbol], Set[BlockMemberSymbol]) = + def createMetadata(d: Defn, ctx: LifterCtx): (Set[BlockMemberSymbol], Set[BlockMemberSymbol]) = var clsSymToBms: Map[Local, BlockMemberSymbol] = Map.empty var modules: Set[BlockMemberSymbol] = Set.empty @@ -300,7 +313,12 @@ class Lifter(using State, Raise): defn match case c: ClsLikeDefn => clsSymToBms += c.isym -> c.sym - if c.k is syntax.Mod then modules += c.sym + if modOrObj(c) then modules += c.sym + if c.k is syntax.Mod then + raise(WarningReport( + msg"Modules are not yet properly lifted and will break." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) case _ => () super.applyDefn(defn) walker.applyDefn(d) @@ -326,15 +344,16 @@ class Lifter(using State, Raise): case Call(Value.Ref(_: BlockMemberSymbol), args) => args.map(applyArg) r - case Instantiate(Select(Value.Ref(_: BlockMemberSymbol), Tree.Ident("class")), args) => + case Instantiate(InstSel(_), args) => args.map(applyPath) r + case _ => super.applyResult(r) override def applyValue(v: Value): Value = v match - case RefOfBms(l) if clsSyms.contains(l) && !l.isModule => + case RefOfBms(l) if clsSyms.contains(l) && !modOrObj(ctx.defns(l)) => raise(WarningReport( - msg"Cannot yet lift the class `${l.nme}` as it is used as a higher-order class." -> N :: Nil, + msg"Cannot yet lift the class `${l.nme}` as it is used as a first-order class." -> N :: Nil, N, Diagnostic.Source.Compilation )) unliftable += l @@ -346,7 +365,7 @@ class Lifter(using State, Raise): extension (b: Block) private def floatOut(ctx: LifterCtx) = - b.floatOutDefns(preserve = defn => ctx.isModule(defn.sym) || ctx.ignored(defn.sym)) + b.floatOutDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = @@ -374,7 +393,7 @@ class Lifter(using State, Raise): case _ => Map.empty else val modLocal = d match - case c: ClsLikeDefn if c.k is syntax.Mod => parentCls match + case c: ClsLikeDefn if modOrObj(c) => parentCls match case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) case _ => N @@ -420,26 +439,26 @@ class Lifter(using State, Raise): override def applyResult(r: Result): Result = r match // if possible, directly rewrite the call using the efficient version case c @ Call(RefOfBms(l), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModule(l) => + case Some(info) if !ctx.isModOrObj(l) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyArg(_)) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) case _ => super.applyResult(r) - case c @ Instantiate(Select(RefOfBms(l), Tree.Ident("class")), args) => + case c @ Instantiate(InstSel(l), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModule(l) => + case Some(info) if !ctx.isModOrObj(l) => val extraArgs = getCallArgs(l, ctx) val newArgs = args.map(applyPath(_)).map(_.asArg) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) case _ => super.applyResult(r) // if possible, directly create the bms and replace the result with it - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => createCall(l, ctx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier override def applyPath(p: Path): Path = p match - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModule(l) => + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => val newSym = syms.get(l) match case None => val newSym = FlowSymbol(l.nme + "$this") @@ -499,7 +518,8 @@ class Lifter(using State, Raise): ctx.getIsymPath(t.owner.get) match case Some(value) if !belongsToCtor(value) => Select(value.asPath, t.id)(N) case _ => super.applyPath(p) - case s @ Select(qual, ident) => s.symbol.flatMap(ctx.getLocalPath) match + case s @ Select(qual, ident) => + s.symbol.flatMap(ctx.getLocalPath) match case Some(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) case _ => super.applyPath(p) case Value.Ref(l) => ctx.getLocalCaptureSym(l) match @@ -507,7 +527,7 @@ class Lifter(using State, Raise): Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) case None => ctx.getLocalPath(l) match case Some(value) => Value.Ref(value) - case None => super.applyPath(p) + case None => super.applyPath(p) case _ => super.applyPath(p) // rewrites references to block member symbols @@ -628,7 +648,7 @@ class Lifter(using State, Raise): Lifted(mainDefn, auxDefn :: extras) - case c: ClsLikeDefn if !(c.k is syntax.Mod) => + case c: ClsLikeDefn if !modOrObj(c) => val newDef = c.copy( owner = N, auxParams = c.auxParams.appended(PlainParamList(extraParams)) ) @@ -648,7 +668,7 @@ class Lifter(using State, Raise): inline def toPaths(l: List[Local]) = l.map(_.asPath) var curSym = TempSymbol(None, "tmp") - val inst = Instantiate(c.sym.asPath, paramArgs) + val inst = Instantiate(Select(c.sym.asPath, Tree.Ident("class"))(N), paramArgs) var acc = blk => Assign(curSym, inst, blk) for ps <- auxSyms do val call = Call(curSym.asPath, ps.map(_.asPath.asArg))(true, false) @@ -686,10 +706,15 @@ class Lifter(using State, Raise): val auxCtorDefn = BlockTransformer(subst).applyFunDefn(auxCtorDefn_) Lifted(lifted, extras ::: (fakeCtorDefn :: auxCtorDefn :: Nil)) - case c: ClsLikeDefn => // module + case c: ClsLikeDefn if modOrObj(c) => // module or object // force it to be a class + val newK = c.k match + case Mod => syntax.Mod + case Obj => syntax.Cls + case _ => c.k // unreachable + val newDef = c.copy( - k = syntax.Cls, paramsOpt = N, + k = newK, paramsOpt = N, owner = N, auxParams = PlainParamList(extraParams) :: Nil ) liftDefnsInCls(newDef, newCtx) @@ -704,7 +729,7 @@ class Lifter(using State, Raise): val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) val modPaths: Map[Local, Local] = ctorIncluded.map: - case c: ClsLikeDefn if c.k is syntax.Mod => ctx.getBmsReqdInfo(c.sym) match + case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) case _ => None @@ -751,7 +776,7 @@ class Lifter(using State, Raise): val (ignored, included) = nested.partition(d => ctx.ignored(d.sym)) val modPaths: Map[Local, Local] = nested.map: - case c: ClsLikeDefn if c.k is syntax.Mod => ctx.getBmsReqdInfo(c.sym) match + case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) case _ => None @@ -829,7 +854,7 @@ class Lifter(using State, Raise): val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => - val (unliftable, modules) = createMetadata(d) + val (unliftable, modules) = createMetadata(d, ctx) val ctxx = ctx.addIgnored(unliftable).addModules(modules) val Lifted(lifted, extra) = d match case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, ctxx))) @@ -1160,7 +1185,7 @@ class UsedVarAnalyzer(b: Block)(using State): args.map(super.applyArg(_)) handleCalledBms(l) r - case Instantiate(Select(RefOfBms(l), Tree.Ident("class")), args) => + case Instantiate(InstSel(l), args) => args.map(super.applyPath(_)) handleCalledBms(l) r @@ -1172,7 +1197,7 @@ class UsedVarAnalyzer(b: Block)(using State): case None => super.applyPath(p) case Some(defn) => val isMod = defn match - case c: ClsLikeDefn => c.k is syntax.Mod + case c: ClsLikeDefn => modOrObj(c) case _ => false if isMod then super.applyPath(p) else @@ -1204,7 +1229,7 @@ class UsedVarAnalyzer(b: Block)(using State): case _ => super.applyPath(p) override def applyDefn(defn: Defn): Defn = defn match - case c: ClsLikeDefn if c.k is syntax.Mod => + case c: ClsLikeDefn if modOrObj(c) => handleCalledBms(c.sym) 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 7cc31d582..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -7,31 +7,5 @@ :global :d -:sjs -class A(x) with - module M with - fun getB() = x - fun getA() = M.getB() -//│ Elab: { Cls AParamList(‹›,List(Param(‹›,class:A‹374›.x,None)),None) { ‹› fun member:getA‹371›() = (class:A‹374›#666.)M‹member:M‹372››.getB‹member:getB‹370››(); Mod M { ‹module› fun member:getB‹370›() = class:A‹374›.x#666; }; }; } -//│ JS (unsanitized): -//│ let A1; -//│ A1 = function A(x1) { -//│ return new A.class(x1); -//│ }; -//│ A1.class = class A { -//│ constructor(x) { -//│ this.x = x; -//│ const this$A = this; -//│ this.M = class M { -//│ static {} -//│ static getB() { -//│ return this$A.x -//│ } -//│ static toString() { return "M"; } -//│ }; -//│ } -//│ getA() { -//│ return this.M.getB() -//│ } -//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } -//│ }; + + diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls index ebcd550a0..863c13fea 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls @@ -10,13 +10,13 @@ class A(x) with //│ let B1, A1, B$ctor, B$; //│ B$ = function B$(A$instance$0, y) { //│ let tmp; -//│ tmp = new B1(y); +//│ tmp = new B1.class(y); //│ return tmp(A$instance$0) //│ }; //│ B$ctor = function B$ctor(A$instance$0) { //│ return (y) => { //│ let tmp; -//│ tmp = new B1(y); +//│ tmp = new B1.class(y); //│ return tmp(A$instance$0) //│ } //│ }; @@ -66,13 +66,13 @@ class A(x) with //│ let B3, A3, B$ctor1, B$1; //│ B$1 = function B$(A$instance$0, y) { //│ let tmp1; -//│ tmp1 = new B3(y); +//│ tmp1 = new B3.class(y); //│ return tmp1(A$instance$0) //│ }; //│ B$ctor1 = function B$ctor(A$instance$0) { //│ return (y) => { //│ let tmp1; -//│ tmp1 = new B3(y); +//│ tmp1 = new B3.class(y); //│ return tmp1(A$instance$0) //│ } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 1e78dfd06..02cfe2a73 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -24,13 +24,13 @@ f(1).get() //│ let Test1, f1, tmp1, Test$ctor, Test$; //│ Test$ = function Test$(x$0) { //│ let tmp2; -//│ tmp2 = new Test1(); +//│ tmp2 = new Test1.class(); //│ return tmp2(x$0) //│ }; //│ Test$ctor = function Test$ctor(x$0) { //│ return () => { //│ let tmp2; -//│ tmp2 = new Test1(); +//│ tmp2 = new Test1.class(); //│ return tmp2(x$0) //│ } //│ }; @@ -93,13 +93,13 @@ f(1, 2).get() //│ }; //│ Test$1 = function Test$(used1$0, a) { //│ let tmp3; -//│ tmp3 = new Test3(a); +//│ tmp3 = new Test3.class(a); //│ return tmp3(used1$0) //│ }; //│ Test$ctor1 = function Test$ctor(used1$0) { //│ return (a) => { //│ let tmp3; -//│ tmp3 = new Test3(a); +//│ tmp3 = new Test3.class(a); //│ return tmp3(used1$0) //│ } //│ }; @@ -144,7 +144,7 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. //│ = 1 :expect 1 @@ -160,7 +160,7 @@ fun f(used1, unused1) = f(1, 2).get() //│ = 1 -:todo +:sjs :expect 1 fun f(used1, unused1) = fun g(g_arg) = @@ -172,7 +172,61 @@ fun f(used1, unused1) = fun get() = used1 new Test f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ JS (unsanitized): +//│ let h3, Test8, g3, f5, tmp5, Test$ctor3, Test$3, g$3, h$3; +//│ h$3 = function h$(used3) { +//│ return used3 +//│ }; +//│ h3 = function h(used3) { +//│ return () => { +//│ return h$3(used3) +//│ } +//│ }; +//│ g$3 = function g$(used1, g_arg) { +//│ let used3, tmp6; +//│ used3 = 2; +//│ tmp6 = h$3(used3); +//│ return used1 + tmp6 +//│ }; +//│ g3 = function g(used1) { +//│ return (g_arg) => { +//│ return g$3(used1, g_arg) +//│ } +//│ }; +//│ Test$3 = function Test$(used1$0) { +//│ let tmp6; +//│ tmp6 = new Test8.class(); +//│ return tmp6(used1$0) +//│ }; +//│ Test$ctor3 = function Test$ctor(used1$0) { +//│ return () => { +//│ let tmp6; +//│ tmp6 = new Test8.class(); +//│ return tmp6(used1$0) +//│ } +//│ }; +//│ Test8 = function Test(used1$01) { +//│ return new Test.class()(used1$01); +//│ }; +//│ Test8.class = class Test7 { +//│ constructor() { +//│ return (used1$0) => { +//│ this.used1$0 = used1$0; +//│ return this; +//│ } +//│ } +//│ get() { +//│ return this.used1$0 +//│ } +//│ toString() { return "Test"; } +//│ }; +//│ f5 = function f(used1, unused1) { +//│ let unused2; +//│ unused2 = 2; +//│ return Test$3(used1) +//│ }; +//│ tmp5 = f5(1, 2); +//│ runtime.safeCall(tmp5.get()) //│ = 1 :sjs @@ -187,13 +241,13 @@ f(1) //│ let A1, f6, A$ctor, A$, f$capture3; //│ A$ = function A$(f$capture$0) { //│ let tmp6; -//│ tmp6 = new A1(); +//│ tmp6 = new A1.class(); //│ return tmp6(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { //│ let tmp6; -//│ tmp6 = new A1(); +//│ tmp6 = new A1.class(); //│ return tmp6(f$capture$0) //│ } //│ }; @@ -256,13 +310,13 @@ f().foo() //│ let Bad1, Good1, f7, tmp6, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; //│ Good$ = function Good$(x$1, y$2, f$capture$0) { //│ let tmp7; -//│ tmp7 = new Good1(); +//│ tmp7 = new Good1.class(); //│ return tmp7(x$1, y$2, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { //│ return () => { //│ let tmp7; -//│ tmp7 = new Good1(); +//│ tmp7 = new Good1.class(); //│ return tmp7(x$1, y$2, f$capture$0) //│ } //│ }; @@ -291,13 +345,13 @@ f().foo() //│ }; //│ Bad$ = function Bad$(f$capture$0) { //│ let tmp7; -//│ tmp7 = new Bad1(); +//│ tmp7 = new Bad1.class(); //│ return tmp7(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { //│ let tmp7; -//│ tmp7 = new Bad1(); +//│ tmp7 = new Bad1.class(); //│ return tmp7(f$capture$0) //│ } //│ }; @@ -399,7 +453,7 @@ fun foo(x, n) = fun foo() = class C() C -//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a higher-order class. +//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-order class. :todo :expect "NN" @@ -414,7 +468,7 @@ fun f() = fun foo() = Test foo()(0) f().get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. //│ = 0 :sjs @@ -428,7 +482,7 @@ fun f(x) = let foo = Test foo() f(2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. //│ JS (unsanitized): //│ let h4, f10, tmp13, h$4; //│ h$4 = function h$(Test$instance, x) { @@ -440,11 +494,11 @@ f(2).get() //│ } //│ }; //│ f10 = function f(x) { -//│ let Test10, foo3; -//│ Test10 = function Test() { +//│ let Test11, foo3; +//│ Test11 = function Test() { //│ return new Test.class(); //│ }; -//│ Test10.class = class Test9 { +//│ Test11.class = class Test10 { //│ constructor() {} //│ get() { //│ h4 = function h() { @@ -454,12 +508,12 @@ f(2).get() //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; -//│ foo3 = Test10; +//│ foo3 = Test11; //│ return runtime.safeCall(foo3()) //│ }; //│ tmp13 = f10(2); //│ runtime.safeCall(tmp13.get()) -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a higher-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. //│ = 2 :fixme diff --git a/hkmc2/shared/src/test/mlscript/lifter/Modules.mls b/hkmc2/shared/src/test/mlscript/lifter/Modules.mls deleted file mode 100644 index acf4ac226..000000000 --- a/hkmc2/shared/src/test/mlscript/lifter/Modules.mls +++ /dev/null @@ -1,114 +0,0 @@ -:lift -:js - -// the problem with all of these tests is that a Ref to a module's own ModuleSymbol -// (let's say M) emits `M` instead of `this` in the codegen - -:sjs -fun foo(x, y) = - module M with - val test = 2 - fun foo() = - set y = 2 - x + y + test - M.foo() -//│ JS (unsanitized): -//│ let M1, foo, foo$capture1; -//│ M1 = function M(x$11, foo$capture$01) { -//│ return new M.class()(x$11, foo$capture$01); -//│ }; -//│ M1.class = class M { -//│ constructor() { -//│ return (x$1, foo$capture$0) => { -//│ this.x$1 = x$1; -//│ this.foo$capture$0 = foo$capture$0; -//│ this.test = 2; -//│ return this; -//│ } -//│ } -//│ foo() { -//│ let tmp; -//│ M.foo$capture$0.y0$ = 2; -//│ tmp = M.x$1 + M.foo$capture$0.y0$; -//│ return tmp + M.test -//│ } -//│ toString() { return "M"; } -//│ }; -//│ foo$capture1 = function foo$capture(y0$1) { -//│ return new foo$capture.class(y0$1); -//│ }; -//│ foo$capture1.class = class foo$capture { -//│ constructor(y0$) { -//│ this.y0$ = y0$; -//│ } -//│ toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } -//│ }; -//│ foo = function foo(x, y) { -//│ let M$, capture; -//│ capture = new foo$capture1(y); -//│ M$ = M1(x, capture); -//│ return M$.foo() -//│ }; - -:fixme -:expect 12 -foo(10, 0) -//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' - -fun foo(x, y) = - module M with - fun foo() = - set y = 2 - x + y - fun foo = M.foo() - foo - -:fixme -:expect 12 -foo(10, 0) -//│ ═══[RUNTIME ERROR] TypeError: Cannot set properties of undefined (setting 'y0$') -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' - - -:sjs -class A(x) with - module M with - fun getB() = x - fun getA() = M.getB() -//│ JS (unsanitized): -//│ let M5, A1; -//│ M5 = function M(A$instance$01) { -//│ return new M.class()(A$instance$01); -//│ }; -//│ M5.class = class M4 { -//│ constructor() { -//│ return (A$instance$0) => { -//│ this.A$instance$0 = A$instance$0; -//│ return this; -//│ } -//│ } -//│ getB() { -//│ return M4.A$instance$0.x -//│ } -//│ toString() { return "M"; } -//│ }; -//│ A1 = function A(x1) { -//│ return new A.class(x1); -//│ }; -//│ A1.class = class A { -//│ constructor(x) { -//│ this.x = x; -//│ this.M$ = M5(this); -//│ } -//│ getA() { -//│ return this.M$.getB() -//│ } -//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } -//│ }; - -:fixme -:expect 2 -A(2).getA() -//│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'x') -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls new file mode 100644 index 000000000..e171c0b8f --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -0,0 +1,129 @@ +:lift +:js + +// the problem with all of these tests is that a Ref to a module's own ModuleSymbol +// (let's say M) emits `M` instead of `this` in the codegen + +:todo +fun foo(x, y) = + module M with + val test = 2 + fun foo() = + set y = 2 + x + y + test + M.foo() +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let M1, foo, foo$capture1;try { M1 = class M { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp; M.foo$capture$0.y0$ = 2; tmp = M.x$1 + M.foo$capture$0.y0$; return tmp + M.test } static toString() { return "M"; } }; foo$capture1 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture1.class = class foo$capture { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$, capture; capture = new foo$capture1(y); M$ = runtime.checkCall(M1(x, capture)); return runtime.checkCall(M$.foo()) }; block$res1 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement + +:todo +:expect 14 +foo(10, 0) +//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' + +:todo +fun foo(x, y) = + module M with + fun foo() = + set y = 2 + x + y + fun foo = M.foo() + foo +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let foo1, M3, foo2, foo$, foo$capture3;try { foo$ = function foo$(...args) { globalThis.Predef.checkArgs("foo$", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture4 = args[2]; return runtime.checkCall(M4.foo()) }; foo1 = function foo(...args) { globalThis.Predef.checkArgs("foo", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture4 = args[2]; return (...args1) => { globalThis.Predef.checkArgs("", 0, true, args1.length); return runtime.checkCall(foo$(M4, x, foo$capture4)) } }; M3 = class M2 { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); M2.foo$capture$0.y0$ = 2; return M2.x$1 + M2.foo$capture$0.y0$ } static toString() { return "M"; } }; foo$capture3 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture3.class = class foo$capture2 { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let tmp, M$, capture; capture = new foo$capture3(y); M$ = runtime.checkCall(M3(x, capture)); tmp = runtime.checkCall(foo$(M$, x, capture)); return tmp }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement + +:fixme +:expect 12 +foo(10, 0) +//│ ═══[RUNTIME ERROR] ReferenceError: foo2 is not defined +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' + + +:todo +class A(x) with + module M with + fun getB() = x + fun getA() = M.getB() +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let M5, A1;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A1 = function A(...args1) { return new A.class(...args1); }; A1.class = class A { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res5 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement + +:todo +:expect 2 +A(2).getA() +//│ ═══[RUNTIME ERROR] ReferenceError: A1 is not defined +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + + +:todo +fun foo(x, y) = + object M with + val test = 2 + fun foo() = + set y = 2 + x + y + test + M.foo() + +:todo +:expect 14 +foo(10, 0) +//│ = 14 + +:todo +fun foo(x, y) = + object M with + fun foo() = + set y = 2 + x + y + fun foo = M.foo() + foo + +:expect 12 +foo(10, 0) +//│ = 12 + + +:sjs +class A(x) with + object M with + fun getB() = x + fun getA() = M.getB() +//│ JS (unsanitized): +//│ let M11, A3; +//│ M11 = function M(A$instance$01) { +//│ return new M.class()(A$instance$01); +//│ }; +//│ M11.class = class M10 { +//│ constructor() { +//│ return (A$instance$0) => { +//│ this.A$instance$0 = A$instance$0; +//│ return this; +//│ } +//│ } +//│ getB() { +//│ return this.A$instance$0.x +//│ } +//│ toString() { return "M"; } +//│ }; +//│ A3 = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ A3.class = class A2 { +//│ constructor(x) { +//│ this.x = x; +//│ this.M$ = M11(this); +//│ } +//│ getA() { +//│ return this.M$.getB() +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; + +:expect 2 +A(2).getA() +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 6d5883838..0c98a18eb 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -24,13 +24,13 @@ hi(0) //│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, hi$capture1, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, handleBlock$$capture1, lambda$capture1; //│ Cont$$ = function Cont$$(n$1, hi$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$3(pc); +//│ tmp = new Cont$3.class(pc); //│ return tmp(n$1, hi$capture$0) //│ }; //│ Cont$$ctor = function Cont$$ctor(n$1, hi$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$3(pc); +//│ tmp = new Cont$3.class(pc); //│ return tmp(n$1, hi$capture$0) //│ } //│ }; @@ -105,25 +105,25 @@ hi(0) //│ }; //│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { //│ let tmp; -//│ tmp = new StackDelay$1(); +//│ tmp = new StackDelay$1.class(); //│ return tmp(handleBlock$$capture$0) //│ }; //│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { //│ return () => { //│ let tmp; -//│ tmp = new StackDelay$1(); +//│ tmp = new StackDelay$1.class(); //│ return tmp(handleBlock$$capture$0) //│ } //│ }; //│ Cont$$2 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$5(pc); +//│ tmp = new Cont$5.class(pc); //│ return tmp(StackDelay$$instance$1, lambda$capture$0) //│ }; //│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$5(pc); +//│ tmp = new Cont$5.class(pc); //│ return tmp(StackDelay$$instance$1, lambda$capture$0) //│ } //│ }; @@ -214,13 +214,13 @@ hi(0) //│ }; //│ Cont$$1 = function Cont$$(handleBlock$$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$4(pc); +//│ tmp = new Cont$4.class(pc); //│ return tmp(handleBlock$$capture$0) //│ }; //│ Cont$$ctor1 = function Cont$$ctor(handleBlock$$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$4(pc); +//│ tmp = new Cont$4.class(pc); //│ return tmp(handleBlock$$capture$0) //│ } //│ }; @@ -308,13 +308,13 @@ sum(10000) //│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, sum$capture1, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, handleBlock$$capture3, lambda$capture3; //│ Cont$$3 = function Cont$$(n$1, curDepth$2, sum$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$9(pc); +//│ tmp = new Cont$9.class(pc); //│ return tmp(n$1, curDepth$2, sum$capture$0) //│ }; //│ Cont$$ctor3 = function Cont$$ctor(n$1, curDepth$2, sum$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$9(pc); +//│ tmp = new Cont$9.class(pc); //│ return tmp(n$1, curDepth$2, sum$capture$0) //│ } //│ }; @@ -410,25 +410,25 @@ sum(10000) //│ }; //│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { //│ let tmp; -//│ tmp = new StackDelay$3(); +//│ tmp = new StackDelay$3.class(); //│ return tmp(handleBlock$$capture$0) //│ }; //│ StackDelay$$ctor1 = function StackDelay$$ctor(handleBlock$$capture$0) { //│ return () => { //│ let tmp; -//│ tmp = new StackDelay$3(); +//│ tmp = new StackDelay$3.class(); //│ return tmp(handleBlock$$capture$0) //│ } //│ }; //│ Cont$$5 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$11(pc); +//│ tmp = new Cont$11.class(pc); //│ return tmp(StackDelay$$instance$1, lambda$capture$0) //│ }; //│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$11(pc); +//│ tmp = new Cont$11.class(pc); //│ return tmp(StackDelay$$instance$1, lambda$capture$0) //│ } //│ }; @@ -519,13 +519,13 @@ sum(10000) //│ }; //│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { //│ let tmp; -//│ tmp = new Cont$10(pc); +//│ tmp = new Cont$10.class(pc); //│ return tmp(handleBlock$$capture$0) //│ }; //│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$10(pc); +//│ tmp = new Cont$10.class(pc); //│ return tmp(handleBlock$$capture$0) //│ } //│ }; From 87edcebf04a72c4d877fada089e8445b572741d7 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 13:33:14 +0800 Subject: [PATCH 069/127] Update warning --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 2 +- hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 0dc387dcb..31ce0fab7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -353,7 +353,7 @@ class Lifter(using State, Raise): override def applyValue(v: Value): Value = v match case RefOfBms(l) if clsSyms.contains(l) && !modOrObj(ctx.defns(l)) => raise(WarningReport( - msg"Cannot yet lift the class `${l.nme}` as it is used as a first-order class." -> N :: Nil, + msg"Cannot yet lift the class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation )) unliftable += l diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 02cfe2a73..fe09f0833 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -144,7 +144,7 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. //│ = 1 :expect 1 @@ -453,7 +453,7 @@ fun foo(x, n) = fun foo() = class C() C -//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-order class. +//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. :todo :expect "NN" @@ -468,7 +468,7 @@ fun f() = fun foo() = Test foo()(0) f().get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. //│ = 0 :sjs @@ -482,7 +482,7 @@ fun f(x) = let foo = Test foo() f(2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. //│ JS (unsanitized): //│ let h4, f10, tmp13, h$4; //│ h$4 = function h$(Test$instance, x) { @@ -513,7 +513,7 @@ f(2).get() //│ }; //│ tmp13 = f10(2); //│ runtime.safeCall(tmp13.get()) -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-order class. +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. //│ = 2 :fixme From b7bdbccf4fc82fc622542494a3e6170aaeeb72b5 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Mon, 10 Feb 2025 13:35:41 +0800 Subject: [PATCH 070/127] update tests --- .../src/test/mlscript/codegen/Modules.mls | 2 +- .../test/mlscript/lifter/StackSafetyLift.mls | 64 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls index bc32bb716..cc6360dcd 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Modules.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Modules.mls @@ -74,7 +74,7 @@ M.y :re M.oops //│ ╔══[ERROR] Module 'M' does not contain member 'oops' -//│ ║ l.73: M.oops +//│ ║ l.75: M.oops //│ ╙── ^^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'oops' yielded 'undefined' diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 0c98a18eb..c41eac283 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -337,22 +337,22 @@ sum(10000) //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ this.sum$capture$0.stackDelayRes3$ = value$; +//│ this.sum$capture$0.stackDelayRes0$ = value$; //│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp0$ = value$; +//│ this.sum$capture$0.tmp2$ = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { -//│ this.sum$capture$0.scrut1$ = this.n$1 == 0; -//│ if (this.sum$capture$0.scrut1$ === true) { +//│ this.sum$capture$0.scrut3$ = this.n$1 == 0; +//│ if (this.sum$capture$0.scrut3$ === true) { //│ return 0 //│ } else { -//│ this.sum$capture$0.tmp2$ = this.n$1 - 1; +//│ this.sum$capture$0.tmp1$ = this.n$1 - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ this.sum$capture$0.tmp0$ = sum1(this.sum$capture$0.tmp2$); -//│ if (this.sum$capture$0.tmp0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ this.sum$capture$0.tmp2$ = sum1(this.sum$capture$0.tmp1$); +//│ if (this.sum$capture$0.tmp2$ instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp0$, this) +//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp2$, this) //│ } //│ this.pc = 1; //│ continue contLoop; @@ -362,50 +362,50 @@ sum(10000) //│ } else if (this.pc === 2) { //│ break contLoop; //│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp0$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp0$, this.curDepth$2); -//│ return this.n$1 + this.sum$capture$0.tmp0$ +//│ this.sum$capture$0.tmp2$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp2$, this.curDepth$2); +//│ return this.n$1 + this.sum$capture$0.tmp2$ //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ sum$capture1 = function sum$capture(tmp0$1, scrut1$1, tmp2$1, stackDelayRes3$1) { -//│ return new sum$capture.class(tmp0$1, scrut1$1, tmp2$1, stackDelayRes3$1); +//│ sum$capture1 = function sum$capture(stackDelayRes0$1, tmp1$1, tmp2$1, scrut3$1) { +//│ return new sum$capture.class(stackDelayRes0$1, tmp1$1, tmp2$1, scrut3$1); //│ }; //│ sum$capture1.class = class sum$capture { -//│ constructor(tmp0$, scrut1$, tmp2$, stackDelayRes3$) { -//│ this.tmp0$ = tmp0$; -//│ this.scrut1$ = scrut1$; +//│ constructor(stackDelayRes0$, tmp1$, tmp2$, scrut3$) { +//│ this.stackDelayRes0$ = stackDelayRes0$; +//│ this.tmp1$ = tmp1$; //│ this.tmp2$ = tmp2$; -//│ this.stackDelayRes3$ = stackDelayRes3$; +//│ this.scrut3$ = scrut3$; //│ } -//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.tmp0$) + ", " + globalThis.Predef.render(this.scrut1$) + ", " + globalThis.Predef.render(this.tmp2$) + ", " + globalThis.Predef.render(this.stackDelayRes3$) + ")"; } +//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.tmp1$) + ", " + globalThis.Predef.render(this.tmp2$) + ", " + globalThis.Predef.render(this.scrut3$) + ")"; } //│ }; //│ sum1 = function sum(n) { //│ let curDepth, capture; //│ capture = new sum$capture1(null, null, null, null); //│ curDepth = globalThis.Predef.__stackDepth; -//│ capture.stackDelayRes3$ = globalThis.Predef.checkDepth(); -//│ if (capture.stackDelayRes3$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.stackDelayRes3$.tail.next = Cont$$3(n, curDepth, capture, 0); -//│ capture.stackDelayRes3$.tail = capture.stackDelayRes3$.tail.next; -//│ return capture.stackDelayRes3$ +//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); +//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.stackDelayRes0$.tail.next = Cont$$3(n, curDepth, capture, 0); +//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; +//│ return capture.stackDelayRes0$ //│ } -//│ capture.scrut1$ = n == 0; -//│ if (capture.scrut1$ === true) { +//│ capture.scrut3$ = n == 0; +//│ if (capture.scrut3$ === true) { //│ return 0 //│ } else { -//│ capture.tmp2$ = n - 1; +//│ capture.tmp1$ = n - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ capture.tmp0$ = sum1(capture.tmp2$); -//│ if (capture.tmp0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.tmp0$.tail.next = Cont$$3(n, curDepth, capture, 1); -//│ capture.tmp0$.tail = capture.tmp0$.tail.next; -//│ return capture.tmp0$ +//│ capture.tmp2$ = sum1(capture.tmp1$); +//│ if (capture.tmp2$ instanceof globalThis.Predef.__EffectSig.class) { +//│ capture.tmp2$.tail.next = Cont$$3(n, curDepth, capture, 1); +//│ capture.tmp2$.tail = capture.tmp2$.tail.next; +//│ return capture.tmp2$ //│ } -//│ capture.tmp0$ = globalThis.Predef.resetDepth(capture.tmp0$, curDepth); -//│ return n + capture.tmp0$ +//│ capture.tmp2$ = globalThis.Predef.resetDepth(capture.tmp2$, curDepth); +//│ return n + capture.tmp2$ //│ } //│ }; //│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { From ad5a7b66d87e22b7fd10e789cc8d5bbea635a87b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 11 Feb 2025 20:32:12 +0800 Subject: [PATCH 071/127] Fix tarjan's algorithm --- core/shared/main/scala/utils/algorithms.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/shared/main/scala/utils/algorithms.scala b/core/shared/main/scala/utils/algorithms.scala index 35da70909..302838d6b 100644 --- a/core/shared/main/scala/utils/algorithms.scala +++ b/core/shared/main/scala/utils/algorithms.scala @@ -40,14 +40,13 @@ object algorithms { * @return A list of strongly connected components of the graph. */ def partitionScc[A](edges: Iterable[(A, A)], nodes: Iterable[A]): List[List[A]] = { - case class SccNode[A]( val node: A, val id: Int, var num: Int = -1, var lowlink: Int = -1, var visited: Boolean = false, - var processed: Boolean = false + var onStack: Boolean = false ) // pre-process: assign each node an id @@ -69,29 +68,34 @@ object algorithms { var sccs: List[List[A]] = List.empty var i = 0 - def dfs(node: SccNode[A]): Unit = { + def dfs(node: SccNode[A], depth: Int = 0): Unit = { + def printlnsp(s: String) = { + println(s) + } + node.num = i node.lowlink = node.num node.visited = true - stack = node :: stack + stack = node :: stack i += 1 for (n <- neighbours(node.id)) { if (!n.visited) { - dfs(n) + dfs(n, depth + 1) node.lowlink = n.lowlink.min(node.lowlink) - } else if (!n.processed) { + } else if (!n.onStack) { node.lowlink = n.num.min(node.lowlink) } } - node.processed = true if (node.lowlink == node.num) { var scc: List[A] = List.empty var cur = stack.head stack = stack.tail + cur.onStack = true while (cur.id != node.id) { scc = cur.node :: scc cur = stack.head stack = stack.tail + cur.onStack = true } scc = cur.node :: scc sccs = scc :: sccs From ac33e6b6a0fa83c844a632758bfa8926dc737ca9 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 01:13:12 +0800 Subject: [PATCH 072/127] specialize cont class, improve analysis, always desugar lambdas --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 67 +++- .../main/scala/hkmc2/codegen/Lowering.scala | 26 +- .../mlscript/basics/BadMemberProjections.mls | 27 +- .../mlscript/basics/MemberProjections.mls | 14 +- .../test/mlscript/basics/MiscArrayTests.mls | 2 +- .../src/test/mlscript/basics/OpBlocks.mls | 2 +- .../mlscript/basics/PureTermStatements.mls | 2 +- .../src/test/mlscript/bbml/bbCodeGen.mls | 22 +- .../src/test/mlscript/bbml/bbGetters.mls | 10 +- .../src/test/mlscript/codegen/BadNew.mls | 8 +- .../src/test/mlscript/codegen/BuiltinOps.mls | 24 +- .../test/mlscript/codegen/CaseShorthand.mls | 49 ++- .../test/mlscript/codegen/ClassMatching.mls | 10 +- .../test/mlscript/codegen/DelayedLetInit.mls | 4 +- hkmc2/shared/src/test/mlscript/codegen/Do.mls | 2 +- .../test/mlscript/codegen/FieldSymbols.mls | 29 +- .../src/test/mlscript/codegen/Getters.mls | 7 +- .../src/test/mlscript/codegen/Hygiene.mls | 15 +- .../src/test/mlscript/codegen/IfThenElse.mls | 29 +- .../src/test/mlscript/codegen/ImportMLs.mls | 4 +- .../src/test/mlscript/codegen/ImportedOps.mls | 2 +- .../test/mlscript/codegen/InlineLambdas.mls | 19 +- .../src/test/mlscript/codegen/Lambdas.mls | 8 +- .../src/test/mlscript/codegen/OptMatch.mls | 13 +- .../src/test/mlscript/codegen/PartialApps.mls | 27 +- .../src/test/mlscript/codegen/ReboundLet.mls | 2 +- .../test/mlscript/codegen/SanityChecks.mls | 16 +- .../src/test/mlscript/codegen/SetIn.mls | 10 +- .../src/test/mlscript/codegen/Spreads.mls | 2 +- .../src/test/mlscript/codegen/While.mls | 24 +- .../mlscript/handlers/ReturnInHandler.mls | 2 +- .../test/mlscript/handlers/ZCombinator.mls | 4 +- .../src/test/mlscript/interop/Array.mls | 2 +- .../src/test/mlscript/lifter/ClassInClass.mls | 22 ++ .../src/test/mlscript/lifter/ClassInFun.mls | 48 +-- .../test/mlscript/lifter/ModulesObjects.mls | 47 ++- .../test/mlscript/lifter/StackSafetyLift.mls | 364 ++++++++---------- .../src/test/mlscript/nofib/cryptarithm2.mls | 2 +- hkmc2/shared/src/test/mlscript/nofib/cse.mls | 2 +- .../shared/src/test/mlscript/nofib/eliza.mls | 2 +- .../shared/src/test/mlscript/nofib/lambda.mls | 4 +- hkmc2/shared/src/test/mlscript/nofib/life.mls | 2 +- .../src/test/mlscript/parser/PrefixOps.mls | 10 +- .../src/test/mlscript/std/Rendering.mls | 2 +- .../syntax/annotations/Declarations.mls | 4 +- .../ucs/normalization/SimplePairMatches.mls | 14 +- 46 files changed, 584 insertions(+), 423 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 31ce0fab7..2ca828fca 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -20,8 +20,6 @@ import hkmc2.syntax.Mod import hkmc2.syntax.Obj import hkmc2.syntax.Pat -// TODO: modules not working - object Lifter: /** * Describes the free variables of a function that have been accessed by nested definitions. @@ -114,6 +112,12 @@ object Lifter: case c: ClsLikeDefn => (c.k is syntax.Mod) || (c.k is syntax.Obj) case _ => false + // TODO: this is very unclean + def isContClassPth(p: Path)(using s: State) = p match + case Select(Select(Select(Value.Ref(s.globalThisSymbol), Tree.Ident("Predef")), + Tree.Ident("__Cont")), Tree.Ident("class")) => true + case _ => false + /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. @@ -145,7 +149,7 @@ class Lifter(using State, Raise): val accessInfo: Map[BlockMemberSymbol, AccessInfo] = Map.empty, val ignoredDefns: Set[BlockMemberSymbol] = Set.empty, val inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty, - val modules: Set[BlockMemberSymbol] = Set.empty, + val modulesObjs: Set[BlockMemberSymbol] = Set.empty, val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol] = Map.empty, val prevFnLocals: FreeVars = FreeVars.empty, val prevClsDefns: List[ClsLikeDefn] = Nil, @@ -168,11 +172,11 @@ class Lifter(using State, Raise): 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) = modules.contains(b) + def isModOrObj(b: BlockMemberSymbol) = modulesObjs.contains(b) def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) - def addModules(mods: Set[BlockMemberSymbol]) = copy(modules = mods ++ modules) + def addModules(mods: Set[BlockMemberSymbol]) = copy(modulesObjs = mods ++ modulesObjs) def withDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(defns = mp) def withAccesses(mp: Map[BlockMemberSymbol, AccessInfo]) = copy(accessInfo = mp) def withInScopes(mp: Map[BlockMemberSymbol, Set[BlockMemberSymbol]]) = copy(inScopeDefns = mp) @@ -309,8 +313,9 @@ class Lifter(using State, Raise): var modules: Set[BlockMemberSymbol] = Set.empty val walker = new BlockTransformer(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = - defn match + override def applyDefn(defn: Defn): Defn = + if defn === d then super.applyDefn(defn) + else defn match case c: ClsLikeDefn => clsSymToBms += c.isym -> c.sym if modOrObj(c) then modules += c.sym @@ -321,6 +326,7 @@ class Lifter(using State, Raise): )) case _ => () super.applyDefn(defn) + walker.applyDefn(d) val clsSyms = clsSymToBms.values.toSet @@ -364,12 +370,12 @@ class Lifter(using State, Raise): (unliftable, modules) extension (b: Block) - private def floatOut(ctx: LifterCtx) = + private def floatOut(ctx: LifterCtx) = b.floatOutDefns(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, mutated, refdDefns) = ctx.getAccesses(d.sym) + val AccessInfo(accessed, _, refdDefns) = ctx.getAccesses(d.sym) val inScopeRefs = refdDefns.intersect(ctx.inScopeDefns(d.sym)) @@ -378,7 +384,7 @@ class Lifter(using State, Raise): .map(sym => ctx.lookup(sym).get) .toList.sortBy(_.uid) - val refMod = inScopeRefs.intersect(ctx.modules) + val refMod = inScopeRefs.intersect(ctx.modulesObjs) 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) @@ -881,8 +887,9 @@ class UsedVarAnalyzer(b: Block)(using State): 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 + 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 directly nested within another defn (shallow) + nestedIn: Map[BlockMemberSymbol, BlockMemberSymbol], // the definition that a definition is directly nested in ) private def createMetadata: DefnMetadata = var defnsMap: Map[BlockMemberSymbol, Defn] = Map.empty @@ -890,6 +897,7 @@ class UsedVarAnalyzer(b: Block)(using State): 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 nestedIn: Map[BlockMemberSymbol, BlockMemberSymbol] = Map.empty def createMetadataFn(f: FunDefn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = existingVars += (f.sym -> existing) @@ -907,7 +915,9 @@ class UsedVarAnalyzer(b: Block)(using State): defnsMap += (f.sym -> f) definedLocals += (f.sym -> thisVars) - for d <- thisScopeDefns do createMetadataDefn(d, newExisting, newInScope) + for d <- thisScopeDefns do + nestedIn += (d.sym -> f.sym) + createMetadataDefn(d, newExisting, newInScope) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = @@ -940,7 +950,9 @@ class UsedVarAnalyzer(b: Block)(using State): defnsMap += (c.sym -> c) definedLocals += (c.sym -> thisVars) - for d <- thisScopeDefns do createMetadataDefn(d, newExisting, newInScope) + for d <- thisScopeDefns do + nestedIn += (d.sym -> c.sym) + createMetadataDefn(d, newExisting, newInScope) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = @@ -948,9 +960,10 @@ class UsedVarAnalyzer(b: Block)(using State): createMetadataDefn(defn, b.definedVars, Set.empty) defn walker.applyBlock(b) - DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns) + DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedIn) - val DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns) = createMetadata + val DefnMetadata(definedLocals, defnsMap, + existingVars, inScopeDefns, nestedDefns, nestedIn) = createMetadata private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -1002,9 +1015,16 @@ class UsedVarAnalyzer(b: Block)(using State): 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)): - // here, we count the class as "accessing" all its methods (since they could be invoked anywhere) - case (acc, defn) => acc.addRefdDefn(defn.sym) + 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.addOne(defn.sym -> ret) ret @@ -1033,7 +1053,7 @@ class UsedVarAnalyzer(b: Block)(using State): d.sym -> AccessInfo( accessed.intersect(definedVarsDeep), mutated.intersect(definedVarsDeep), - refdDefns.intersect(defnSyms) + refdDefns.intersect(defnSyms) // only care about definitions nested in this top-level definition ) val accessInfoMap = accessInfo.toMap @@ -1154,9 +1174,18 @@ class UsedVarAnalyzer(b: Block)(using State): b case _ => super.applyBlock(b) - def handleCalledBms(l: BlockMemberSymbol) = defnSyms.get(l) match + def handleCalledBms(called: BlockMemberSymbol): Unit = defnSyms.get(called) match case None => () case Some(defn) => + // special case continuation classes + defn match + case c: ClsLikeDefn => c.parentPath match + case S(path) if Lifter.isContClassPth(path) => return + // treat the continuation class as if it does not exist + case _ => () + case _ => () + + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) val muts = muted.intersect(thisVars) val reads = defn.freeVars.intersect(thisVars) -- muts diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index a9d18d6d2..44f64b66f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -583,9 +583,33 @@ class Lowering()(using Config, TL, Raise, State, Ctx): // val resSym = new TermSymbol(N, Tree.Ident("$res")) // def topLevel(t: st): Block = // subTerm(t)(r => codegen.Assign(resSym, r, codegen.End()))(using Subst.empty) + + def desugarLambdas(b: Block) = + def rewriteOneBlk(b: Block) = + var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil + val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): + override def applyValue(v: Value): Value = v match + case lam: Value.Lam => + val sym = BlockMemberSymbol("lambda", Nil) + lambdasList ::= (sym -> super.applyLam(lam)) + Value.Ref(sym) + case _ => super.applyValue(v) + val blk = lambdaRewriter.applyBlock(b) + (blk, lambdasList) + + val transformer = new BlockTransformer(SymbolSubst()): + override def applyBlock(b: Block): Block = + val (newBlk, lambdasList) = rewriteOneBlk(b) + val lambdaDefns = lambdasList.map: + case (sym, Value.Lam(params, body)) => + FunDefn(None, sym, params :: Nil, body) + val ret = lambdaDefns.foldLeft(newBlk): + case (acc, defn) => Define(defn, acc) + super.applyBlock(ret) + transformer.applyBlock(b) def topLevel(t: st): Block = - val res = term(t)(ImplctRet)(using Subst.empty) + val res = desugarLambdas(term(t)(ImplctRet)(using Subst.empty)) val stackSafe = config.stackSafety match case N => res case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index 6dc9bbe70..cc049f619 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -10,27 +10,32 @@ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS (unsanitized): -//│ (self, ...args) => { return runtime.safeCall(self.x(...args)) } -//│ = [function block$res1] +//│ let lambda; +//│ lambda = function lambda(self, ...args) { +//│ return runtime.safeCall(self.x(...args)) +//│ }; +//│ lambda +//│ = [function lambda] :ssjs :e :re 1::x() //│ ╔══[ERROR] Integer literal is not a known class. -//│ ║ l.19: 1::x() +//│ ║ l.23: 1::x() //│ ║ ^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS: -//│ block$res2 = runtime.checkCall(((...args1) => { -//│ globalThis.Predef.checkArgs("", 1, false, args1.length); +//│ lambda1 = function lambda(...args1) { +//│ globalThis.Predef.checkArgs("lambda", 1, false, args1.length); //│ let self = args1[0]; //│ let args = globalThis.Predef.tupleSlice(args1, 1, 0); //│ return runtime.safeCall(self.x(...args)) -//│ })()); +//│ }; +//│ block$res2 = runtime.checkCall(lambda1()); //│ undefined -//│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 fun (::) f(a, b) = [a, b] @@ -47,19 +52,19 @@ let x = 1 :e "A"::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.48: "A"::x +//│ ║ l.53: "A"::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function block$res7] +//│ = [function lambda] :e "A" ::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.57: "A" ::x +//│ ║ l.62: "A" ::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function block$res8] +//│ = [function lambda] diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index 928a2ded4..aba8e2a58 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -46,8 +46,8 @@ M.Foo:: n(foo, 2) :sjs let m = M.Foo::m //│ JS (unsanitized): -//│ let m; m = (self, ...args) => { return self.m(...args) }; -//│ m = [function m] +//│ let m, lambda5; lambda5 = function lambda(self, ...args) { return self.m(...args) }; m = lambda5; +//│ m = [function lambda] m(foo) //│ = 124 @@ -91,7 +91,7 @@ foo.M.Foo::m() //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 :e :re @@ -114,7 +114,7 @@ Foo::m //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function block$res21] +//│ = [function lambda] :e :sjs @@ -128,7 +128,11 @@ Foo::n(foo, 2) //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS (unsanitized): -//│ ((self, ...args) => { return runtime.safeCall(self.n(...args)) })(foo, 2) +//│ let lambda10; +//│ lambda10 = function lambda(self, ...args) { +//│ return runtime.safeCall(self.n(...args)) +//│ }; +//│ lambda10(foo, 2) //│ = 125 diff --git a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls index 38085356a..07281e3ce 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls @@ -21,7 +21,7 @@ xs :re xs.map(x => x * 2) -//│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 1 argument but got 3 xs.map((x, i, a) => x * 2) //│ = [4, 2, 0] diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 2b6107405..924ccbe6b 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -31,7 +31,7 @@ let f = x => x * 2 -//│ f = [function f] +//│ f = [function lambda] 2 + 1 diff --git a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls index 213625fa7..f3f4dea66 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls @@ -21,7 +21,7 @@ case x then x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.19: case x then x //│ ╙── ^ -//│ = [function block$res3] +//│ = [function lambda] :w diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index 5fe9796cf..8d98a6f19 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -53,8 +53,8 @@ false :sjs (x => x): [T] -> T -> T //│ JS (unsanitized): -//│ (x1) => { return x1 } -//│ = [function block$res8] +//│ let lambda; lambda = function lambda(x1) { return x1 }; lambda +//│ = [function lambda] //│ Type: ['T] -> ('T) ->{⊥} 'T @@ -150,7 +150,8 @@ fun pow(x) = case //│ JS (unsanitized): //│ let pow; //│ pow = function pow(x1) { -//│ return (caseScrut) => { +//│ let lambda1; +//│ lambda1 = function lambda(caseScrut) { //│ let n, tmp2, tmp3, tmp4; //│ if (caseScrut === 0) { //│ return 1 @@ -161,7 +162,8 @@ fun pow(x) = case //│ tmp4 = runtime.safeCall(tmp2(tmp3)); //│ return x1 * tmp4 //│ } -//│ } +//│ }; +//│ return lambda1 //│ }; //│ Type: ⊤ @@ -173,7 +175,8 @@ fun not = case //│ JS (unsanitized): //│ let not; //│ not = function not() { -//│ return (caseScrut) => { +//│ let lambda1; +//│ lambda1 = function lambda(caseScrut) { //│ if (caseScrut === true) { //│ return false //│ } else { @@ -183,7 +186,8 @@ fun not = case //│ throw new globalThis.Error("match error"); //│ } //│ } -//│ } +//│ }; +//│ return lambda1 //│ }; //│ Type: ⊤ @@ -204,7 +208,8 @@ fun fact = case //│ JS (unsanitized): //│ let fact; //│ fact = function fact() { -//│ return (caseScrut) => { +//│ let lambda1; +//│ lambda1 = function lambda(caseScrut) { //│ let n, tmp3, tmp4, tmp5; //│ if (caseScrut === 0) { //│ return 1 @@ -215,7 +220,8 @@ fun fact = case //│ tmp5 = tmp3(tmp4); //│ return n * tmp5 //│ } -//│ } +//│ }; +//│ return lambda1 //│ }; //│ Type: ⊤ diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls index 1fdecf090..55b5ba80f 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls @@ -59,7 +59,7 @@ test2 test2() //│ JS (unsanitized): //│ test21() -//│ = [function] +//│ = [function lambda] //│ Type: Int -> Int :ssjs @@ -85,14 +85,16 @@ fun test2() = //│ test22 = function test2() { //│ let funny, tmp1; //│ funny = function funny() { -//│ return (caseScrut) => { +//│ let lambda; +//│ lambda = function lambda(caseScrut) { //│ let n, tmp2, tmp3, tmp4; //│ n = caseScrut; //│ tmp2 = funny(); //│ tmp3 = n - 1; //│ tmp4 = tmp2(tmp3); //│ return tmp4 + 1 -//│ } +//│ }; +//│ return lambda //│ }; //│ tmp1 = funny(); //│ return tmp1 @@ -111,7 +113,7 @@ fun test2() = fun test3 = print("Hi") //│ ╔══[ERROR] Function definition shape not yet supported for test3 -//│ ║ l.112: print("Hi") +//│ ║ l.114: print("Hi") //│ ╙── ^^^^^^^^^^^ //│ Type: ⊤ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 68d6a8c34..ee9ced41c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -11,14 +11,14 @@ new new 2 //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor +// TODO: somehow desugaring lambdas fixed this, is this intended? +:todo :sjs :ge new 2 + 2 //│ JS (unsanitized): -//│ new (arg1, arg2) => { return arg1 + arg2 }(2, 2) -//│ > try { block$res3 = new (...args) => { globalThis.Predef.checkArgs("", 2, true, args.length); let arg1 = args[0]; let arg2 = args[1]; return arg1 + arg2 }(2, 2); undefined } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Malformed arrow function parameter list +//│ let lambda; lambda = function lambda(arg1, arg2) { return arg1 + arg2 }; new lambda(2, 2) +//│ = [object Object] :re new() diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls index 8ef6e2554..ca6dfb914 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls @@ -20,7 +20,7 @@ //│ = 2 + -//│ = [function block$res4] +//│ = [function lambda] // * A bit confusing... but at least there's a warning! :w @@ -42,7 +42,7 @@ //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' (+) -//│ = [function block$res8] +//│ = [function lambda] (+)(2, 3) //│ = 5 @@ -59,7 +59,7 @@ //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' id(+) -//│ = [function] +//│ = [function lambda] id(+)(1, 2) //│ = 3 @@ -74,8 +74,13 @@ id(+)(1, 2) :re id(+)(1) //│ JS (unsanitized): -//│ let tmp1; tmp1 = Predef.id((arg1, arg2) => { return arg1 + arg2 }); runtime.safeCall(tmp1(1)) -//│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 +//│ let tmp1, lambda4; +//│ lambda4 = function lambda(arg1, arg2) { +//│ return arg1 + arg2 +//│ }; +//│ tmp1 = Predef.id(lambda4); +//│ runtime.safeCall(tmp1(1)) +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 fun (+) lol(a, b) = [a, b] @@ -90,7 +95,12 @@ fun (+) lol(a, b) = [a, b] :sjs id(~)(2) //│ JS (unsanitized): -//│ let tmp2; tmp2 = Predef.id((arg) => { return ~ arg }); runtime.safeCall(tmp2(2)) +//│ let tmp2, lambda5; +//│ lambda5 = function lambda(arg) { +//│ return ~ arg +//│ }; +//│ tmp2 = Predef.id(lambda5); +//│ runtime.safeCall(tmp2(2)) //│ = -3 2 |> ~ @@ -98,7 +108,7 @@ id(~)(2) typeof -//│ = [function block$res21] +//│ = [function lambda] typeof(1) //│ = "number" diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index a677b6718..77f0d4526 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -2,32 +2,42 @@ case x then x -//│ = [function block$res1] +//│ = [function lambda] :sjs case { x then x } //│ JS (unsanitized): -//│ (caseScrut) => { let x; x = caseScrut; return x } -//│ = [function block$res2] +//│ let lambda1; lambda1 = function lambda(caseScrut) { let x; x = caseScrut; return x }; lambda1 +//│ = [function lambda] :sjs x => if x is 0 then true //│ JS (unsanitized): -//│ (x) => { if (x === 0) { return true } else { throw new this.Error("match error"); } } -//│ = [function block$res3] +//│ let lambda2; +//│ lambda2 = function lambda(x) { +//│ if (x === 0) { +//│ return true +//│ } else { +//│ throw new globalThis.Error("match error"); +//│ } +//│ }; +//│ lambda2 +//│ = [function lambda] :sjs case 0 then true //│ JS (unsanitized): -//│ (caseScrut) => { +//│ let lambda3; +//│ lambda3 = function lambda(caseScrut) { //│ if (caseScrut === 0) { //│ return true //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } -//│ } -//│ = [function block$res4] +//│ }; +//│ lambda3 +//│ = [function lambda] (case x then x) of 1 //│ = 1 @@ -53,8 +63,16 @@ case 0 then true _ then false //│ JS (unsanitized): -//│ (caseScrut) => { if (caseScrut === 0) { return true } else { return false } } -//│ = [function block$res10] +//│ let lambda10; +//│ lambda10 = function lambda(caseScrut) { +//│ if (caseScrut === 0) { +//│ return true +//│ } else { +//│ return false +//│ } +//│ }; +//│ lambda10 +//│ = [function lambda] class Some(value) module None @@ -64,18 +82,19 @@ val isDefined = case Some then true None then false //│ JS (unsanitized): -//│ let isDefined, tmp5; -//│ tmp5 = (caseScrut) => { +//│ let isDefined, tmp5, lambda11; +//│ lambda11 = function lambda(caseScrut) { //│ if (caseScrut instanceof Some1.class) { //│ return true //│ } else { //│ if (caseScrut instanceof None1) { //│ return false //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; +//│ tmp5 = lambda11; //│ isDefined = tmp5; -//│ isDefined = [function tmp5] +//│ isDefined = [function lambda] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index d69830c3b..8b6c193a5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -57,17 +57,19 @@ if s is :sjs x => if x is Some(x) then x //│ JS (unsanitized): -//│ (x3) => { +//│ let lambda; +//│ lambda = function lambda(x3) { //│ let param04, x4; //│ if (x3 instanceof Some1.class) { //│ param04 = x3.value; //│ x4 = param04; //│ return x4 //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } -//│ } -//│ = [function block$res9] +//│ }; +//│ lambda +//│ = [function lambda] class C(a) diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index 454535763..484072966 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -55,8 +55,8 @@ f let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f1; f1 = (x1) => { return x1 + 1 }; -//│ f = [function f1] +//│ let f1, lambda; lambda = function lambda(x1) { return x1 + 1 }; f1 = lambda; +//│ f = [function lambda] f(1) //│ JS (unsanitized): diff --git a/hkmc2/shared/src/test/mlscript/codegen/Do.mls b/hkmc2/shared/src/test/mlscript/codegen/Do.mls index 1a64369cb..976fa4df8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Do.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Do.mls @@ -44,7 +44,7 @@ val f = case //│ > let res = "other" //│ > let $doTemp = (member:Predef#666.)print‹member:print›(res#666) //│ > else res#666 -//│ f = [function tmp1] +//│ f = [function lambda] f(0) //│ = "null" diff --git a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls index 6adcd0a2b..312b4ee82 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls @@ -88,17 +88,19 @@ case //│ Lowered: //│ Program: //│ imports = Nil -//│ main = Assign: -//│ lhs = $block$res -//│ rhs = Lam: -//│ params = ParamList: -//│ flags = ParamListFlags of false -//│ params = Ls of -//│ Param: -//│ flags = () -//│ sym = caseScrut -//│ sign = N -//│ restParam = N +//│ main = Define: +//│ defn = FunDefn: +//│ owner = N +//│ sym = member:lambda +//│ params = Ls of +//│ ParamList: +//│ flags = ParamListFlags of false +//│ params = Ls of +//│ Param: +//│ flags = () +//│ sym = caseScrut +//│ sign = N +//│ restParam = N //│ body = Match: //│ scrut = Ref of caseScrut //│ arms = Ls of @@ -126,10 +128,13 @@ case //│ args = Ls of //│ Lit of StrLit of "match error" //│ rest = End of "" +//│ rest = Assign: \ +//│ lhs = $block$res +//│ rhs = Ref of member:lambda //│ rest = Return: \ //│ res = Lit of UnitLit of false //│ implct = true -//│ = [function block$res6] +//│ = [function lambda] // TODO support: diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index 2d0c44aef..9f8eef33e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -156,21 +156,22 @@ fun baz() = //│ JS (unsanitized): //│ let baz; //│ baz = function baz() { -//│ let w, z; +//│ let w, z, lambda; //│ w = function w() { //│ return 1 //│ }; //│ z = function z() { //│ return 2 //│ }; -//│ return (x, y) => { +//│ lambda = function lambda(x, y) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = x + y; //│ tmp2 = w(); //│ tmp3 = tmp1 + tmp2; //│ tmp4 = z(); //│ return tmp3 + tmp4 -//│ } +//│ }; +//│ return lambda //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 9e8e89ace..58e316ba8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -54,9 +54,16 @@ let f = () => x let x = 2 f() //│ JS (unsanitized): -//│ let x, f, x1; x = 1; f = () => { return x }; x1 = 2; runtime.safeCall(f()) +//│ let x, f, x1, lambda; +//│ x = 1; +//│ lambda = function lambda() { +//│ return x +//│ }; +//│ f = lambda; +//│ x1 = 2; +//│ runtime.safeCall(f()) //│ = 1 -//│ f = [function f] +//│ f = [function lambda] //│ x = 2 @@ -66,10 +73,10 @@ module Test with val x = 1 let x = 2 //│ ╔══[ERROR] Name 'x' is already used -//│ ║ l.67: let x = 2 +//│ ║ l.74: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.66: val x = 1 +//│ ║ l.73: val x = 1 //│ ╙── ^^^^^ //│ JS: //│ Test4 = class Test3 { diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index 2eb6f8637..08271033b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -9,15 +9,16 @@ if true then 1 else 0 :sjs let f = x => if x then print("ok") else print("ko") //│ JS (unsanitized): -//│ let f; -//│ f = (x) => { +//│ let f, lambda; +//│ lambda = function lambda(x) { //│ if (x === true) { //│ return Predef.print("ok") //│ } else { //│ return Predef.print("ko") //│ } //│ }; -//│ f = [function f] +//│ f = lambda; +//│ f = [function lambda] f(true) //│ > ok @@ -29,8 +30,8 @@ f(false) :sjs let f = x => print((if x then "ok" else "ko") + "!") //│ JS (unsanitized): -//│ let f1, tmp; -//│ tmp = (x) => { +//│ let f1, tmp, lambda1; +//│ lambda1 = function lambda(x) { //│ let tmp1, tmp2; //│ if (x === true) { //│ tmp1 = "ok"; @@ -40,14 +41,15 @@ let f = x => print((if x then "ok" else "ko") + "!") //│ tmp2 = tmp1 + "!"; //│ return Predef.print(tmp2) //│ }; +//│ tmp = lambda1; //│ f1 = tmp; -//│ f = [function tmp] +//│ f = [function lambda] :sjs let f = x => print((if x and x then "ok" else "ko") + "!") //│ JS (unsanitized): -//│ let f2, tmp1; -//│ tmp1 = (x) => { +//│ let f2, tmp1, lambda2; +//│ lambda2 = function lambda(x) { //│ let tmp2, tmp3; //│ if (x === true) { //│ tmp2 = "ok"; @@ -57,8 +59,9 @@ let f = x => print((if x and x then "ok" else "ko") + "!") //│ tmp3 = tmp2 + "!"; //│ return Predef.print(tmp3) //│ }; +//│ tmp1 = lambda2; //│ f2 = tmp1; -//│ f = [function tmp1] +//│ f = [function lambda] // --- TODO: What we want --- // this.f = (x) => { // let tmp, tmp1, flag; @@ -88,18 +91,18 @@ f(false) x => if x > 0 then print("Hi") else print("Bye") -//│ = [function block$res9] +//│ = [function lambda] x => print(if true then "Hi" else "Bye") -//│ = [function block$res10] +//│ = [function lambda] x => print(if x + 1 > 0 then "Hi" else "Bye") -//│ = [function block$res11] +//│ = [function lambda] x => let str = (+)(if x + 1 > 0 then "Hello" else "Bye", "World") print(str) -//│ = [function block$res12] +//│ = [function lambda] fun f(x, y) = diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls index 5a32d9a10..3f726617c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls @@ -28,10 +28,10 @@ None isDefined() //│ = false case Some(x) then x -//│ = [function block$res8] +//│ = [function lambda] case { Some(x) then x } -//│ = [function block$res9] +//│ = [function lambda] Some(1) isDefined() //│ = true diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index 5d625f575..0b9b570de 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -24,7 +24,7 @@ let name = "_" //│ name = "_" let display(balance) = balance -//│ display = [function display] +//│ display = [function lambda] "b" ~ display("-") //│ = "b-" diff --git a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls index 69239798f..0cb831a19 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls @@ -3,21 +3,23 @@ :sjs (x => x + 1 + 1 + 1 + 1 + 1)(1) //│ JS (unsanitized): -//│ ((x) => { +//│ let lambda; +//│ lambda = function lambda(x) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = x + 1; //│ tmp1 = tmp + 1; //│ tmp2 = tmp1 + 1; //│ tmp3 = tmp2 + 1; //│ return tmp3 + 1 -//│ })(1) +//│ }; +//│ lambda(1) //│ = 6 :sjs (x => x + 1 + 1 + 1 + 1 + 1 + 1)(1) //│ JS (unsanitized): -//│ let tmp; -//│ tmp = (x) => { +//│ let tmp, lambda1; +//│ lambda1 = function lambda(x) { //│ let tmp1, tmp2, tmp3, tmp4, tmp5; //│ tmp1 = x + 1; //│ tmp2 = tmp1 + 1; @@ -26,19 +28,20 @@ //│ tmp5 = tmp4 + 1; //│ return tmp5 + 1 //│ }; +//│ tmp = lambda1; //│ tmp(1) //│ = 7 :sjs (x => x) + 1 //│ JS (unsanitized): -//│ ((x) => { return x }) + 1 -//│ = "(...args) => { globalThis.Predef.checkArgs(\"\", 1, true, args.length); let x = args[0]; return x }1" +//│ let lambda2; lambda2 = function lambda(x) { return x }; lambda2 + 1 +//│ = "function lambda(...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }1" :sjs 1 + (x => x) //│ JS (unsanitized): -//│ 1 + ((x) => { return x }) -//│ = "1(...args) => { globalThis.Predef.checkArgs(\"\", 1, true, args.length); let x = args[0]; return x }" +//│ let lambda3; lambda3 = function lambda(x) { return x }; 1 + lambda3 +//│ = "1function lambda(...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls index c866bdf69..e25c32269 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls @@ -8,14 +8,14 @@ x => let y = x y //│ JS (unsanitized): -//│ (x) => { let y; y = x; return y } -//│ = [function block$res1] +//│ let lambda; lambda = function lambda(x) { let y; y = x; return y }; lambda +//│ = [function lambda] :todo (acc, _) => acc //│ JS (unsanitized): -//│ (acc, _) => { return acc } -//│ = [function block$res2] +//│ let lambda1; lambda1 = function lambda(acc, _) { return acc }; lambda1 +//│ = [function lambda] diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index 59a2a1056..6dae27f9c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -35,8 +35,8 @@ val isDefined = case Some(_) then true None then false //│ JS (unsanitized): -//│ let isDefined1, tmp1; -//│ tmp1 = (caseScrut) => { +//│ let isDefined1, tmp1, lambda; +//│ lambda = function lambda(caseScrut) { //│ let param0; //│ if (caseScrut instanceof Some1.class) { //│ param0 = caseScrut.value; @@ -45,12 +45,13 @@ val isDefined = case //│ if (caseScrut instanceof None1.class) { //│ return false //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ } //│ }; +//│ tmp1 = lambda; //│ isDefined1 = tmp1; -//│ isDefined = [function tmp1] +//│ isDefined = [function lambda] isDefined(Some(1)) //│ = true @@ -62,7 +63,7 @@ isDefined(None) val isDefined = x => if x is Some(_) then true None then false -//│ isDefined = [function tmp3] +//│ isDefined = [function lambda] isDefined(Some(1)) //│ = true @@ -77,7 +78,7 @@ module Foo with val isOther = x => if x is Foo.Other(_) then true None then false -//│ isOther = [function tmp5] +//│ isOther = [function lambda] fun keepIfGreaterThan(x, y) = diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 95e50c401..59abac5cf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -4,7 +4,7 @@ fun foo(x, y, z) = [x, y, z] let f = foo(1, _, 3) -//│ f = [function f] +//│ f = [function lambda] f(2) //│ = [1, 2, 3] @@ -12,14 +12,14 @@ f(2) :sjs let f = foo(1, _, _) //│ JS (unsanitized): -//│ let f1; f1 = (_, _1) => { return foo(1, _, _1) }; -//│ f = [function f1] +//│ let f1, lambda1; lambda1 = function lambda(_, _1) { return foo(1, _, _1) }; f1 = lambda1; +//│ f = [function lambda] f(2, 3) //│ = [1, 2, 3] let g = f(_, 3) -//│ g = [function g] +//│ g = [function lambda] g(2) //│ = [1, 2, 3] @@ -45,14 +45,14 @@ foo(..._) let h = _ - 2 -//│ h = [function h] +//│ h = [function lambda] h(1) //│ = -1 let i = _(0, 1, _) -//│ i = [function i] +//│ i = [function lambda] i((x, y, z) => x + y + z, 2) //│ = 3 @@ -97,12 +97,12 @@ let j = _.x(123) :todo let j = _.x(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function j] +//│ j = [function lambda] :todo // really support this? let j = _.x.y(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function j1] +//│ j = [function lambda] class C(a, b, c) @@ -120,7 +120,7 @@ class C(a, b, c) _ - _ of 1, 2 -//│ = [function block$res33] +//│ = [function lambda] (_ - _) of 1, 2 //│ = -1 @@ -151,13 +151,18 @@ _ - 2 <| 1 :sjs 1 . _ - 2 //│ JS (unsanitized): -//│ ((_) => { return Predef.passTo(1, _) }) - 2 +//│ let lambda30; lambda30 = function lambda(_) { return Predef.passTo(1, _) }; lambda30 - 2 //│ = NaN :sjs 1 . (_ - 2)() //│ JS (unsanitized): -//│ let tmp7; tmp7 = Predef.passTo(1, (_) => { return _ - 2 }); runtime.safeCall(tmp7()) +//│ let tmp7, lambda31; +//│ lambda31 = function lambda(_) { +//│ return _ - 2 +//│ }; +//│ tmp7 = Predef.passTo(1, lambda31); +//│ runtime.safeCall(tmp7()) //│ = -1 1 . (_ - _)(2) diff --git a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls index a6ac31261..2ec569b4a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls @@ -15,7 +15,7 @@ foo() let x = 1 let f = () => x -//│ f = [function f] +//│ f = [function lambda] //│ x = 1 f() diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index 46c1ba8e7..2e310da61 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -62,7 +62,12 @@ f(3)(4) :noSanityCheck let f = (x, y) => x + y in f(2) //│ JS: -//│ f3 = (x, y) => { return x + y }; block$res5 = runtime.safeCall(f3(2)); undefined +//│ lambda = function lambda(x, y) { +//│ return x + y +//│ }; +//│ f3 = lambda; +//│ block$res5 = runtime.safeCall(f3(2)); +//│ undefined //│ = NaN :ssjs @@ -70,16 +75,17 @@ let f = (x, y) => x + y in f(2) let f = (x, y) => x + y f(2) //│ JS: -//│ f4 = (...args) => { -//│ globalThis.Predef.checkArgs("", 2, true, args.length); +//│ lambda1 = function lambda(...args) { +//│ globalThis.Predef.checkArgs("lambda", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y //│ }; +//│ f4 = lambda1; //│ block$res6 = runtime.safeCall(f4(2)); //│ undefined -//│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 -//│ f = [function f4] +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 +//│ f = [function lambda] :expect NaN diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 62d979933..2cd10bf09 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -78,11 +78,12 @@ example() //│ JS (unsanitized): //│ let example2; //│ example2 = function example() { -//│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12; +//│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, lambda; //│ x2 = 0; -//│ get_x = () => { +//│ lambda = function lambda() { //│ return x2 //│ }; +//│ get_x = lambda; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; @@ -118,11 +119,12 @@ example() //│ JS (unsanitized): //│ let example3; //│ example3 = function example() { -//│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; +//│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, lambda; //│ x2 = 0; -//│ get_x = () => { +//│ lambda = function lambda() { //│ return x2 //│ }; +//│ get_x = lambda; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index 63ac713cf..3f2157499 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -28,7 +28,7 @@ foo(...a) let f = (...xs) => xs -//│ f = [function f] +//│ f = [function lambda] f(a) //│ = [[1, 2, 3]] diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index abf3e4f42..77b1bc608 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -4,7 +4,8 @@ :sjs () => (while true then 0) //│ JS (unsanitized): -//│ () => { +//│ let lambda; +//│ lambda = function lambda() { //│ let scrut, tmp; //│ tmp1: while (true) { //│ scrut = true; @@ -12,17 +13,18 @@ //│ tmp = 0; //│ continue tmp1; //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ break; //│ } //│ return tmp -//│ } -//│ = [function block$res1] +//│ }; +//│ lambda +//│ = [function lambda] () => while true then 0 -//│ = [function block$res2] +//│ = [function lambda] let x = true @@ -103,7 +105,8 @@ while let i = 0 i < 10 do set i += 1 //│ JS (unsanitized): -//│ () => { +//│ let lambda2; +//│ lambda2 = function lambda() { //│ let i2, scrut3, tmp19, tmp20; //│ tmp21: while (true) { //│ i2 = 0; @@ -119,8 +122,9 @@ while //│ break; //│ } //│ return tmp20 -//│ } -//│ = [function block$res13] +//│ }; +//│ lambda2 +//│ = [function lambda] let i = 0 in @@ -244,14 +248,14 @@ f(Cons(1, Cons(2, Cons(3, 0)))) () => while true then 0 -//│ = [function block$res26] +//│ = [function lambda] :fixme while print("Hello World"); false then 0(0) else 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.251: then 0(0) +//│ ║ l.255: then 0(0) //│ ╙── ^^^^ //│ ═══[ERROR] Unrecognized term split (false literal). //│ > Hello World diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 4c724b153..0113000d0 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -35,7 +35,7 @@ let l = () => fun foo()(r) = r(()) return 3 4 -//│ l = [function l] +//│ l = [function lambda] l() //│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls index 7347e57a6..ca572fe74 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls @@ -10,7 +10,7 @@ fun mkrec(g) = let fact = mkrec of self => x => if x == 0 then 1 else self(x - 1) * x -//│ fact = [function] +//│ fact = [function lambda] fact(3) //│ = 6 @@ -31,7 +31,7 @@ fun mkrec(g) = let fact = mkrec of self => x => if x == 0 then 1 else self(x - 1) * x -//│ fact = [function] +//│ fact = [function lambda] :stackSafe 1000 fact(10000) diff --git a/hkmc2/shared/src/test/mlscript/interop/Array.mls b/hkmc2/shared/src/test/mlscript/interop/Array.mls index e322dac46..ec81be309 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Array.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Array.mls @@ -8,7 +8,7 @@ let arr = [true, false] // * Array methods annoyingly supply extra arguments, such as the index and the array itself :re arr.map(_ === false) -//│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 +//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 1 argument but got 3 :todo arr.map((e, ...) => e === false) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls index 863c13fea..316596e23 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls @@ -117,3 +117,25 @@ class A(x) with A(1).getA() //│ ═══[RUNTIME ERROR] TypeError: this.B is not a function //│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' + +:sjs +class A with + val x = + fun g() = 2 + g +//│ JS (unsanitized): +//│ let g, A5, g$; +//│ g$ = function g$(A$instance) { +//│ return 2 +//│ }; +//│ g = function g(A$instance) { +//│ return () => { +//│ return g$(A$instance) +//│ } +//│ }; +//│ A5 = class A4 { +//│ constructor() { +//│ this.x = g; +//│ } +//│ toString() { return "A"; } +//│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index fe09f0833..1db1aea6a 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -238,7 +238,7 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f6, A$ctor, A$, f$capture3; +//│ let A1, f6, A$ctor, A$, f$capture1; //│ A$ = function A$(f$capture$0) { //│ let tmp6; //│ tmp6 = new A1.class(); @@ -269,10 +269,10 @@ f(1) //│ } //│ toString() { return "A(" + "" + ")"; } //│ }; -//│ f$capture3 = function f$capture(x0$1) { +//│ f$capture1 = function f$capture(x0$1) { //│ return new f$capture.class(x0$1); //│ }; -//│ f$capture3.class = class f$capture2 { +//│ f$capture1.class = class f$capture { //│ constructor(x0$) { //│ this.x0$ = x0$; //│ } @@ -280,7 +280,7 @@ f(1) //│ }; //│ f6 = function f(x) { //│ let tmp6, tmp7, capture; -//│ capture = new f$capture3(x); +//│ capture = new f$capture1(x); //│ tmp6 = A$(capture); //│ tmp7 = runtime.safeCall(tmp6.f()); //│ return capture.x0$ @@ -307,38 +307,39 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f7, tmp6, Bad$ctor, Bad$, Good$ctor, Good$, f$capture5; -//│ Good$ = function Good$(x$1, y$2, f$capture$0) { +//│ let Bad1, Good1, f7, tmp6, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; +//│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { //│ let tmp7; //│ tmp7 = new Good1.class(); -//│ return tmp7(x$1, y$2, f$capture$0) +//│ return tmp7(x$1, y$2, z$3, f$capture$0) //│ }; -//│ Good$ctor = function Good$ctor(x$1, y$2, f$capture$0) { +//│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { //│ let tmp7; //│ tmp7 = new Good1.class(); -//│ return tmp7(x$1, y$2, f$capture$0) +//│ return tmp7(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { -//│ return (x$11, y$21, f$capture$01) => { -//│ return new Good.class()(x$11, y$21, f$capture$01); +//│ return (x$11, y$21, z$31, f$capture$01) => { +//│ return new Good.class()(x$11, y$21, z$31, f$capture$01); //│ } //│ }; //│ Good1.class = class Good { //│ constructor() { -//│ return (x$1, y$2, f$capture$0) => { +//│ return (x$1, y$2, z$3, f$capture$0) => { //│ this.x$1 = x$1; //│ this.y$2 = y$2; +//│ this.z$3 = z$3; //│ this.f$capture$0 = f$capture$0; //│ return this; //│ } //│ } //│ foo() { //│ let tmp7, tmp8; -//│ this.f$capture$0.z1$ = 100; +//│ this.z$3 = 100; //│ tmp7 = this.x$1 + this.y$2; -//│ tmp8 = tmp7 + this.f$capture$0.z1$; +//│ tmp8 = tmp7 + this.z$3; //│ return tmp8 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } @@ -373,26 +374,25 @@ f().foo() //│ } //│ toString() { return "Bad(" + "" + ")"; } //│ }; -//│ f$capture5 = function f$capture(w0$1, z1$1) { -//│ return new f$capture.class(w0$1, z1$1); +//│ f$capture3 = function f$capture(w0$1) { +//│ return new f$capture.class(w0$1); //│ }; -//│ f$capture5.class = class f$capture4 { -//│ constructor(w0$, z1$) { +//│ f$capture3.class = class f$capture2 { +//│ constructor(w0$) { //│ this.w0$ = w0$; -//│ this.z1$ = z1$; //│ } -//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ", " + globalThis.Predef.render(this.z1$) + ")"; } +//│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } //│ }; //│ f7 = function f() { -//│ let x, y, tmp7, tmp8, capture; -//│ capture = new f$capture5(null, null); +//│ let x, y, z, tmp7, tmp8, capture; +//│ capture = new f$capture3(null); //│ x = 1; //│ y = 10; -//│ capture.z1$ = 10; +//│ z = 10; //│ capture.w0$ = 1000; //│ tmp7 = Bad$(capture); //│ tmp8 = runtime.safeCall(tmp7.foo()); -//│ return Good$(x, y, capture) +//│ return Good$(x, y, z, capture) //│ }; //│ tmp6 = f7(); //│ runtime.safeCall(tmp6.foo()) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index e171c0b8f..c98cd5d30 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -13,8 +13,9 @@ fun foo(x, y) = x + y + test M.foo() //│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M1, foo, foo$capture1;try { M1 = class M { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp; M.foo$capture$0.y0$ = 2; tmp = M.x$1 + M.foo$capture$0.y0$; return tmp + M.test } static toString() { return "M"; } }; foo$capture1 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture1.class = class foo$capture { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$, capture; capture = new foo$capture1(y); M$ = runtime.checkCall(M1(x, capture)); return runtime.checkCall(M$.foo()) }; block$res1 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let M1, foo;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp; M.y$1 = 2; tmp = M.x$0 + M.y$1; return tmp + M.test } static toString() { return "M"; } }; foo = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res1 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement :todo @@ -32,7 +33,8 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let foo1, M3, foo2, foo$, foo$capture3;try { foo$ = function foo$(...args) { globalThis.Predef.checkArgs("foo$", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture4 = args[2]; return runtime.checkCall(M4.foo()) }; foo1 = function foo(...args) { globalThis.Predef.checkArgs("foo", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture4 = args[2]; return (...args1) => { globalThis.Predef.checkArgs("", 0, true, args1.length); return runtime.checkCall(foo$(M4, x, foo$capture4)) } }; M3 = class M2 { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); M2.foo$capture$0.y0$ = 2; return M2.x$1 + M2.foo$capture$0.y0$ } static toString() { return "M"; } }; foo$capture3 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture3.class = class foo$capture2 { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let tmp, M$, capture; capture = new foo$capture3(y); M$ = runtime.checkCall(M3(x, capture)); tmp = runtime.checkCall(foo$(M$, x, capture)); return tmp }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let foo1, M3, foo2, foo$, foo$capture1;try { foo$ = function foo$(...args) { globalThis.Predef.checkArgs("foo$", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture2 = args[2]; return runtime.checkCall(M4.foo()) }; foo1 = function foo(...args) { globalThis.Predef.checkArgs("foo", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture2 = args[2]; return (...args1) => { globalThis.Predef.checkArgs("", 0, true, args1.length); return runtime.checkCall(foo$(M4, x, foo$capture2)) } }; M3 = class M2 { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); M2.foo$capture$0.y0$ = 2; return M2.x$1 + M2.foo$capture$0.y0$ } static toString() { return "M"; } }; foo$capture1 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture1.class = class foo$capture { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let tmp, M$, capture; capture = new foo$capture1(y); M$ = runtime.checkCall(M3(x, capture)); tmp = runtime.checkCall(foo$(M$, x, capture)); return tmp }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement @@ -49,6 +51,7 @@ class A(x) with fun getB() = x fun getA() = M.getB() //│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ ═══[WARNING] Modules are not yet properly lifted and will break. //│ > let M5, A1;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A1 = function A(...args1) { return new A.class(...args1); }; A1.class = class A { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res5 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement @@ -127,3 +130,41 @@ class A(x) with :expect 2 A(2).getA() //│ = 2 + +// Classes and functions in modules // + +:expect 2 +module M with + val x = 2 + class A() with + fun get = x + val hi = A() +M.hi.get +//│ = 2 + +:expect 2 +module M with + val x = 2 + fun f() = + fun g() = x + g +M.f()() +//│ = 2 + +:expect 2 +object M with + val x = 2 + class A() with + fun get = x + val hi = A() +M.hi.get +//│ = 2 + +:expect 2 +object M with + val x = 2 + fun f() = + fun g() = x + g +M.f()() +//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index c41eac283..0431acdb5 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -21,48 +21,50 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, hi$capture1, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, handleBlock$$capture1, lambda$capture1; -//│ Cont$$ = function Cont$$(n$1, hi$capture$0, pc) { +//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, handleBlock$$capture1; +//│ Cont$$ = function Cont$$(n$0, scrut$1, tmp$2, stackDelayRes$3, pc) { //│ let tmp; //│ tmp = new Cont$3.class(pc); -//│ return tmp(n$1, hi$capture$0) +//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) //│ }; -//│ Cont$$ctor = function Cont$$ctor(n$1, hi$capture$0) { +//│ Cont$$ctor = function Cont$$ctor(n$0, scrut$1, tmp$2, stackDelayRes$3) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$3.class(pc); -//│ return tmp(n$1, hi$capture$0) +//│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) //│ } //│ }; //│ Cont$3 = function Cont$(pc1) { -//│ return (n$11, hi$capture$01) => { -//│ return new Cont$.class(pc1)(n$11, hi$capture$01); +//│ return (n$01, scrut$11, tmp$21, stackDelayRes$31) => { +//│ return new Cont$.class(pc1)(n$01, scrut$11, tmp$21, stackDelayRes$31); //│ } //│ }; //│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (n$1, hi$capture$0) => { +//│ return (n$0, scrut$1, tmp$2, stackDelayRes$3) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.n$1 = n$1; -//│ this.hi$capture$0 = hi$capture$0; +//│ this.n$0 = n$0; +//│ this.scrut$1 = scrut$1; +//│ this.tmp$2 = tmp$2; +//│ this.stackDelayRes$3 = stackDelayRes$3; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ this.hi$capture$0.stackDelayRes0$ = value$; +//│ this.stackDelayRes$3 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { -//│ this.hi$capture$0.scrut1$ = this.n$1 == 0; -//│ if (this.hi$capture$0.scrut1$ === true) { +//│ this.scrut$1 = this.n$0 == 0; +//│ if (this.scrut$1 === true) { //│ return 0 //│ } else { -//│ this.hi$capture$0.tmp2$ = this.n$1 - 1; +//│ this.tmp$2 = this.n$0 - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi(this.hi$capture$0.tmp2$) +//│ return hi(this.tmp$2) //│ } //│ this.pc = 1; //│ continue contLoop; @@ -74,33 +76,21 @@ hi(0) //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ hi$capture1 = function hi$capture(stackDelayRes0$1, scrut1$1, tmp2$1) { -//│ return new hi$capture.class(stackDelayRes0$1, scrut1$1, tmp2$1); -//│ }; -//│ hi$capture1.class = class hi$capture { -//│ constructor(stackDelayRes0$, scrut1$, tmp2$) { -//│ this.stackDelayRes0$ = stackDelayRes0$; -//│ this.scrut1$ = scrut1$; -//│ this.tmp2$ = tmp2$; -//│ } -//│ toString() { return "hi$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.scrut1$) + ", " + globalThis.Predef.render(this.tmp2$) + ")"; } -//│ }; //│ hi = function hi(n) { -//│ let capture; -//│ capture = new hi$capture1(null, null, null); -//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); -//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.stackDelayRes0$.tail.next = Cont$$(n, capture, 0); -//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; -//│ return capture.stackDelayRes0$ -//│ } -//│ capture.scrut1$ = n == 0; -//│ if (capture.scrut1$ === true) { +//│ let scrut, tmp, stackDelayRes; +//│ stackDelayRes = globalThis.Predef.checkDepth(); +//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { +//│ stackDelayRes.tail.next = Cont$$(n, scrut, tmp, stackDelayRes, 0); +//│ stackDelayRes.tail = stackDelayRes.tail.next; +//│ return stackDelayRes +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { //│ return 0 //│ } else { -//│ capture.tmp2$ = n - 1; +//│ tmp = n - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ return hi(capture.tmp2$) +//│ return hi(tmp) //│ } //│ }; //│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { @@ -115,76 +105,66 @@ hi(0) //│ return tmp(handleBlock$$capture$0) //│ } //│ }; -//│ Cont$$2 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { +//│ Cont$$2 = function Cont$$(StackDelay$$instance$1, res$0, pc) { //│ let tmp; //│ tmp = new Cont$5.class(pc); -//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ return tmp(StackDelay$$instance$1, res$0) //│ }; -//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { +//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$1, res$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$5.class(pc); -//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ return tmp(StackDelay$$instance$1, res$0) //│ } //│ }; //│ Cont$5 = function Cont$(pc1) { -//│ return (StackDelay$$instance$11, lambda$capture$01) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$11, lambda$capture$01); +//│ return (StackDelay$$instance$11, res$01) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$11, res$01); //│ } //│ }; //│ Cont$5.class = class Cont$1 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (StackDelay$$instance$1, lambda$capture$0) => { +//│ return (StackDelay$$instance$1, res$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; //│ this.StackDelay$$instance$1 = StackDelay$$instance$1; -//│ this.lambda$capture$0 = lambda$capture$0; +//│ this.res$0 = res$0; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 4) { -//│ this.lambda$capture$0.res0$ = value$; +//│ this.res$0 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 4) { -//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return this.lambda$capture$0.res0$ +//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { +//│ return this.res$0 //│ } //│ this.pc = 5; //│ continue contLoop; //│ } else if (this.pc === 5) { -//│ return this.lambda$capture$0.res0$ +//│ return this.res$0 //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ lambda$capture1 = function lambda$capture(res0$1) { -//│ return new lambda$capture.class(res0$1); -//│ }; -//│ lambda$capture1.class = class lambda$capture { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; //│ lambda$ = function lambda$(StackDelay$$instance, resume) { -//│ let capture; -//│ capture = new lambda$capture1(null); +//│ let res1; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ capture.res0$ = resume(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$2(StackDelay$$instance, capture, 4); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$ +//│ res1 = resume(); +//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { +//│ res1.tail.next = Cont$$2(StackDelay$$instance, res1, 4); +//│ res1.tail = res1.tail.next; +//│ return res1 //│ } -//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res0$ +//│ if (res1 instanceof globalThis.Predef.__Return.class) { +//│ return res1 //│ } -//│ return capture.res0$ +//│ return res1 //│ }; //│ lambda = function lambda(StackDelay$$instance) { //│ return (resume) => { @@ -212,79 +192,78 @@ hi(0) //│ } //│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ Cont$$1 = function Cont$$(handleBlock$$capture$0, pc) { +//│ Cont$$1 = function Cont$$(res$0, pc) { //│ let tmp; //│ tmp = new Cont$4.class(pc); -//│ return tmp(handleBlock$$capture$0) +//│ return tmp(res$0) //│ }; -//│ Cont$$ctor1 = function Cont$$ctor(handleBlock$$capture$0) { +//│ Cont$$ctor1 = function Cont$$ctor(res$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$4.class(pc); -//│ return tmp(handleBlock$$capture$0) +//│ return tmp(res$0) //│ } //│ }; //│ Cont$4 = function Cont$(pc1) { -//│ return (handleBlock$$capture$01) => { -//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ return (res$01) => { +//│ return new Cont$.class(pc1)(res$01); //│ } //│ }; //│ Cont$4.class = class Cont$2 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (handleBlock$$capture$0) => { +//│ return (res$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ this.res$0 = res$0; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 2) { -//│ this.handleBlock$$capture$0.res1$ = value$; +//│ this.res$0 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 2) { -//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return this.handleBlock$$capture$0.res1$ +//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { +//│ return this.res$0 //│ } //│ this.pc = 3; //│ continue contLoop; //│ } else if (this.pc === 3) { -//│ return this.handleBlock$$capture$0.res1$ +//│ return this.res$0 //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ handleBlock$$capture1 = function handleBlock$$capture(stackHandler0$1, res1$1) { -//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ handleBlock$$capture1 = function handleBlock$$capture(stackHandler0$1) { +//│ return new handleBlock$$capture.class(stackHandler0$1); //│ }; //│ handleBlock$$capture1.class = class handleBlock$$capture { -//│ constructor(stackHandler0$, res1$) { +//│ constructor(stackHandler0$) { //│ this.stackHandler0$ = stackHandler0$; -//│ this.res1$ = res1$; //│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ")"; } //│ }; //│ handleBlock$ = function handleBlock$() { -//│ let capture; -//│ capture = new handleBlock$$capture1(null, null); +//│ let res1, capture; +//│ capture = new handleBlock$$capture1(null); //│ capture.stackHandler0$ = StackDelay$$(capture); //│ globalThis.Predef.__stackLimit = 5; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = capture.stackHandler0$; -//│ capture.res1$ = hi(0); -//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res1$.tail.next = Cont$$1(capture, 2); -//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ res1 = hi(0); +//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { +//│ res1.tail.next = Cont$$1(res1, 2); +//│ return globalThis.Predef.__handleBlockImpl(res1, capture.stackHandler0$) //│ } -//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res1$ +//│ if (res1 instanceof globalThis.Predef.__Return.class) { +//│ return res1 //│ } -//│ return capture.res1$ +//│ return res1 //│ }; //│ res = handleBlock$(); //│ if (res instanceof this.Predef.__EffectSig.class) { @@ -305,54 +284,57 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, sum$capture1, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, handleBlock$$capture3, lambda$capture3; -//│ Cont$$3 = function Cont$$(n$1, curDepth$2, sum$capture$0, pc) { +//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, handleBlock$$capture3; +//│ Cont$$3 = function Cont$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { //│ let tmp; //│ tmp = new Cont$9.class(pc); -//│ return tmp(n$1, curDepth$2, sum$capture$0) +//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ }; -//│ Cont$$ctor3 = function Cont$$ctor(n$1, curDepth$2, sum$capture$0) { +//│ Cont$$ctor3 = function Cont$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$9.class(pc); -//│ return tmp(n$1, curDepth$2, sum$capture$0) +//│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ } //│ }; //│ Cont$9 = function Cont$(pc1) { -//│ return (n$11, curDepth$21, sum$capture$01) => { -//│ return new Cont$.class(pc1)(n$11, curDepth$21, sum$capture$01); +//│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { +//│ return new Cont$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); //│ } //│ }; //│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (n$1, curDepth$2, sum$capture$0) => { +//│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.n$1 = n$1; -//│ this.curDepth$2 = curDepth$2; -//│ this.sum$capture$0 = sum$capture$0; +//│ this.n$0 = n$0; +//│ this.scrut$1 = scrut$1; +//│ this.tmp$2 = tmp$2; +//│ this.tmp$3 = tmp$3; +//│ this.curDepth$4 = curDepth$4; +//│ this.stackDelayRes$5 = stackDelayRes$5; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 0) { -//│ this.sum$capture$0.stackDelayRes0$ = value$; +//│ this.stackDelayRes$5 = value$; //│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp2$ = value$; +//│ this.tmp$3 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 0) { -//│ this.sum$capture$0.scrut3$ = this.n$1 == 0; -//│ if (this.sum$capture$0.scrut3$ === true) { +//│ this.scrut$1 = this.n$0 == 0; +//│ if (this.scrut$1 === true) { //│ return 0 //│ } else { -//│ this.sum$capture$0.tmp1$ = this.n$1 - 1; +//│ this.tmp$2 = this.n$0 - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ this.sum$capture$0.tmp2$ = sum1(this.sum$capture$0.tmp1$); -//│ if (this.sum$capture$0.tmp2$ instanceof globalThis.Predef.__EffectSig.class) { +//│ this.tmp$3 = sum1(this.tmp$2); +//│ if (this.tmp$3 instanceof globalThis.Predef.__EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(this.sum$capture$0.tmp2$, this) +//│ return globalThis.Predef.__appendInCont(this.tmp$3, this) //│ } //│ this.pc = 1; //│ continue contLoop; @@ -362,50 +344,37 @@ sum(10000) //│ } else if (this.pc === 2) { //│ break contLoop; //│ } else if (this.pc === 1) { -//│ this.sum$capture$0.tmp2$ = globalThis.Predef.resetDepth(this.sum$capture$0.tmp2$, this.curDepth$2); -//│ return this.n$1 + this.sum$capture$0.tmp2$ +//│ this.tmp$3 = globalThis.Predef.resetDepth(this.tmp$3, this.curDepth$4); +//│ return this.n$0 + this.tmp$3 //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ sum$capture1 = function sum$capture(stackDelayRes0$1, tmp1$1, tmp2$1, scrut3$1) { -//│ return new sum$capture.class(stackDelayRes0$1, tmp1$1, tmp2$1, scrut3$1); -//│ }; -//│ sum$capture1.class = class sum$capture { -//│ constructor(stackDelayRes0$, tmp1$, tmp2$, scrut3$) { -//│ this.stackDelayRes0$ = stackDelayRes0$; -//│ this.tmp1$ = tmp1$; -//│ this.tmp2$ = tmp2$; -//│ this.scrut3$ = scrut3$; -//│ } -//│ toString() { return "sum$capture(" + globalThis.Predef.render(this.stackDelayRes0$) + ", " + globalThis.Predef.render(this.tmp1$) + ", " + globalThis.Predef.render(this.tmp2$) + ", " + globalThis.Predef.render(this.scrut3$) + ")"; } -//│ }; //│ sum1 = function sum(n) { -//│ let curDepth, capture; -//│ capture = new sum$capture1(null, null, null, null); +//│ let scrut, tmp, tmp1, curDepth, stackDelayRes; //│ curDepth = globalThis.Predef.__stackDepth; -//│ capture.stackDelayRes0$ = globalThis.Predef.checkDepth(); -//│ if (capture.stackDelayRes0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.stackDelayRes0$.tail.next = Cont$$3(n, curDepth, capture, 0); -//│ capture.stackDelayRes0$.tail = capture.stackDelayRes0$.tail.next; -//│ return capture.stackDelayRes0$ -//│ } -//│ capture.scrut3$ = n == 0; -//│ if (capture.scrut3$ === true) { +//│ stackDelayRes = globalThis.Predef.checkDepth(); +//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { +//│ stackDelayRes.tail.next = Cont$$3(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); +//│ stackDelayRes.tail = stackDelayRes.tail.next; +//│ return stackDelayRes +//│ } +//│ scrut = n == 0; +//│ if (scrut === true) { //│ return 0 //│ } else { -//│ capture.tmp1$ = n - 1; +//│ tmp = n - 1; //│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; -//│ capture.tmp2$ = sum1(capture.tmp1$); -//│ if (capture.tmp2$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.tmp2$.tail.next = Cont$$3(n, curDepth, capture, 1); -//│ capture.tmp2$.tail = capture.tmp2$.tail.next; -//│ return capture.tmp2$ +//│ tmp1 = sum1(tmp); +//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { +//│ tmp1.tail.next = Cont$$3(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); +//│ tmp1.tail = tmp1.tail.next; +//│ return tmp1 //│ } -//│ capture.tmp2$ = globalThis.Predef.resetDepth(capture.tmp2$, curDepth); -//│ return n + capture.tmp2$ +//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); +//│ return n + tmp1 //│ } //│ }; //│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { @@ -420,76 +389,66 @@ sum(10000) //│ return tmp(handleBlock$$capture$0) //│ } //│ }; -//│ Cont$$5 = function Cont$$(StackDelay$$instance$1, lambda$capture$0, pc) { +//│ Cont$$5 = function Cont$$(StackDelay$$instance$1, res$0, pc) { //│ let tmp; //│ tmp = new Cont$11.class(pc); -//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ return tmp(StackDelay$$instance$1, res$0) //│ }; -//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$1, lambda$capture$0) { +//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$1, res$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$11.class(pc); -//│ return tmp(StackDelay$$instance$1, lambda$capture$0) +//│ return tmp(StackDelay$$instance$1, res$0) //│ } //│ }; //│ Cont$11 = function Cont$(pc1) { -//│ return (StackDelay$$instance$11, lambda$capture$01) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$11, lambda$capture$01); +//│ return (StackDelay$$instance$11, res$01) => { +//│ return new Cont$.class(pc1)(StackDelay$$instance$11, res$01); //│ } //│ }; //│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (StackDelay$$instance$1, lambda$capture$0) => { +//│ return (StackDelay$$instance$1, res$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; //│ this.StackDelay$$instance$1 = StackDelay$$instance$1; -//│ this.lambda$capture$0 = lambda$capture$0; +//│ this.res$0 = res$0; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 5) { -//│ this.lambda$capture$0.res0$ = value$; +//│ this.res$0 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 5) { -//│ if (this.lambda$capture$0.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return this.lambda$capture$0.res0$ +//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { +//│ return this.res$0 //│ } //│ this.pc = 6; //│ continue contLoop; //│ } else if (this.pc === 6) { -//│ return this.lambda$capture$0.res0$ +//│ return this.res$0 //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ lambda$capture3 = function lambda$capture(res0$1) { -//│ return new lambda$capture.class(res0$1); -//│ }; -//│ lambda$capture3.class = class lambda$capture2 { -//│ constructor(res0$) { -//│ this.res0$ = res0$; -//│ } -//│ toString() { return "lambda$capture(" + globalThis.Predef.render(this.res0$) + ")"; } -//│ }; //│ lambda$1 = function lambda$(StackDelay$$instance, resume) { -//│ let capture; -//│ capture = new lambda$capture3(null); +//│ let res2; //│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ capture.res0$ = resume(); -//│ if (capture.res0$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res0$.tail.next = Cont$$5(StackDelay$$instance, capture, 5); -//│ capture.res0$.tail = capture.res0$.tail.next; -//│ return capture.res0$ +//│ res2 = resume(); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$$5(StackDelay$$instance, res2, 5); +//│ res2.tail = res2.tail.next; +//│ return res2 //│ } -//│ if (capture.res0$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res0$ +//│ if (res2 instanceof globalThis.Predef.__Return.class) { +//│ return res2 //│ } -//│ return capture.res0$ +//│ return res2 //│ }; //│ lambda1 = function lambda(StackDelay$$instance) { //│ return (resume) => { @@ -517,79 +476,78 @@ sum(10000) //│ } //│ toString() { return "StackDelay$(" + "" + ")"; } //│ }; -//│ Cont$$4 = function Cont$$(handleBlock$$capture$0, pc) { +//│ Cont$$4 = function Cont$$(res$0, pc) { //│ let tmp; //│ tmp = new Cont$10.class(pc); -//│ return tmp(handleBlock$$capture$0) +//│ return tmp(res$0) //│ }; -//│ Cont$$ctor4 = function Cont$$ctor(handleBlock$$capture$0) { +//│ Cont$$ctor4 = function Cont$$ctor(res$0) { //│ return (pc) => { //│ let tmp; //│ tmp = new Cont$10.class(pc); -//│ return tmp(handleBlock$$capture$0) +//│ return tmp(res$0) //│ } //│ }; //│ Cont$10 = function Cont$(pc1) { -//│ return (handleBlock$$capture$01) => { -//│ return new Cont$.class(pc1)(handleBlock$$capture$01); +//│ return (res$01) => { +//│ return new Cont$.class(pc1)(res$01); //│ } //│ }; //│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { //│ constructor(pc) { -//│ return (handleBlock$$capture$0) => { +//│ return (res$0) => { //│ let tmp; //│ tmp = super(null, null); //│ this.pc = pc; -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; +//│ this.res$0 = res$0; //│ return this; //│ } //│ } //│ resume(value$) { //│ if (this.pc === 3) { -//│ this.handleBlock$$capture$0.res1$ = value$; +//│ this.res$0 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ if (this.handleBlock$$capture$0.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return this.handleBlock$$capture$0.res1$ +//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { +//│ return this.res$0 //│ } //│ this.pc = 4; //│ continue contLoop; //│ } else if (this.pc === 4) { -//│ return this.handleBlock$$capture$0.res1$ +//│ return this.res$0 //│ } //│ break; //│ } //│ } //│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; -//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1, res1$1) { -//│ return new handleBlock$$capture.class(stackHandler0$1, res1$1); +//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1) { +//│ return new handleBlock$$capture.class(stackHandler0$1); //│ }; //│ handleBlock$$capture3.class = class handleBlock$$capture2 { -//│ constructor(stackHandler0$, res1$) { +//│ constructor(stackHandler0$) { //│ this.stackHandler0$ = stackHandler0$; -//│ this.res1$ = res1$; //│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ", " + globalThis.Predef.render(this.res1$) + ")"; } +//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ")"; } //│ }; //│ handleBlock$1 = function handleBlock$() { -//│ let capture; -//│ capture = new handleBlock$$capture3(null, null); +//│ let res2, capture; +//│ capture = new handleBlock$$capture3(null); //│ capture.stackHandler0$ = StackDelay$$1(capture); //│ globalThis.Predef.__stackLimit = 1000; //│ globalThis.Predef.__stackOffset = 0; //│ globalThis.Predef.__stackDepth = 1; //│ globalThis.Predef.__stackHandler = capture.stackHandler0$; -//│ capture.res1$ = sum1(10000); -//│ if (capture.res1$ instanceof globalThis.Predef.__EffectSig.class) { -//│ capture.res1$.tail.next = Cont$$4(capture, 3); -//│ return globalThis.Predef.__handleBlockImpl(capture.res1$, capture.stackHandler0$) +//│ res2 = sum1(10000); +//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { +//│ res2.tail.next = Cont$$4(res2, 3); +//│ return globalThis.Predef.__handleBlockImpl(res2, capture.stackHandler0$) //│ } -//│ if (capture.res1$ instanceof globalThis.Predef.__Return.class) { -//│ return capture.res1$ +//│ if (res2 instanceof globalThis.Predef.__Return.class) { +//│ return res2 //│ } -//│ return capture.res1$ +//│ return res2 //│ }; //│ res1 = handleBlock$1(); //│ if (res1 instanceof this.Predef.__EffectSig.class) { diff --git a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls index 302656e35..15e9e9b3b 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls @@ -36,7 +36,7 @@ fun guard(b) = if b then StateT(s => [Unit, s] :: Nil) else StateT(s => Nil) fun put(s) = StateT(x => [Unit, s] :: Nil) let get = StateT(s => [s, s] :: Nil) -//│ get = StateT([function]) +//│ get = StateT([function lambda]) class Digits(i: List[Int], c: List[[Char, Int]]) diff --git a/hkmc2/shared/src/test/mlscript/nofib/cse.mls b/hkmc2/shared/src/test/mlscript/nofib/cse.mls index cfe6f1803..4011292cc 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cse.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cse.mls @@ -53,7 +53,7 @@ fun update(f) = s => [f(s), s] fun set_(s_) = s => [s_, s] let incr = update(x => x + 1) -//│ incr = [function] +//│ incr = [function lambda] class Node[T](a: T, b: List[Node[T]]) diff --git a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls index 442068269..e8d6125ba 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls @@ -246,7 +246,7 @@ let initial = Nil then Nil [k, rs] :: t then [words(k), cycle(rs)] :: lscomp(t) [lscomp(respMsgs), cycle(repeatMsgs)] -//│ initial = [[[[["C","A","N"],["Y","O","U"]], Lazy([function])],[[["C","A","N"],["I"]], Lazy([function])],[[["Y","O","U"],["A","R","E"]], Lazy([function])],[[["Y","O","U","'","R","E"]], Lazy([function])],[[["I"],["D","O","N","'","T"]], Lazy([function])],[[["I"],["F","E","E","L"]], Lazy([function])],[[["W","H","Y"],["D","O","N","'","T"],["Y","O","U"]], Lazy([function])],[[["W","H","Y"],["C","A","N","'","T"],["I"]], Lazy([function])],[[["A","R","E"],["Y","O","U"]], Lazy([function])],[[["I"],["C","A","N","'","T"]], Lazy([function])],[[["I"],["A","M"]], Lazy([function])],[[["I","'","M"]], Lazy([function])],[[["Y","O","U"]], Lazy([function])],[[["Y","E","S"]], Lazy([function])],[[["N","O"]], Lazy([function])],[[["C","O","M","P","U","T","E","R"]], Lazy([function])],[[["C","O","M","P","U","T","E","R","S"]], Lazy([function])],[[["I"],["W","A","N","T"]], Lazy([function])],[[["W","H","A","T"]], Lazy([function])],[[["H","O","W"]], Lazy([function])],[[["W","H","O"]], Lazy([function])],[[["W","H","E","R","E"]], Lazy([function])],[[["W","H","E","N"]], Lazy([function])],[[["N","A","M","E"]], Lazy([function])],[[["W","H","Y"]], Lazy([function])],[[["C","A","U","S","E"]], Lazy([function])],[[["B","E","C","A","U","S","E"]], Lazy([function])],[[["D","R","E","A","M"]], Lazy([function])],[[["S","O","R","R","Y"]], Lazy([function])],[[["H","I"]], Lazy([function])],[[["D","R","E","A","M","S"]], Lazy([function])],[[["M","A","Y","B","E"]], Lazy([function])],[[["H","E","L","L","O"]], Lazy([function])],[[["A","L","W","A","Y","S"]], Lazy([function])],[[["Y","O","U","R"]], Lazy([function])],[[["A","L","I","K","E"]], Lazy([function])],[[["T","H","I","N","K"]], Lazy([function])],[[["F","R","I","E","N","D","S"]], Lazy([function])],[[["F","R","I","E","N","D"]], Lazy([function])],[[], Lazy([function])]], Lazy([function])] +//│ initial = [[[[["C","A","N"],["Y","O","U"]], Lazy([function lambda])],[[["C","A","N"],["I"]], Lazy([function lambda])],[[["Y","O","U"],["A","R","E"]], Lazy([function lambda])],[[["Y","O","U","'","R","E"]], Lazy([function lambda])],[[["I"],["D","O","N","'","T"]], Lazy([function lambda])],[[["I"],["F","E","E","L"]], Lazy([function lambda])],[[["W","H","Y"],["D","O","N","'","T"],["Y","O","U"]], Lazy([function lambda])],[[["W","H","Y"],["C","A","N","'","T"],["I"]], Lazy([function lambda])],[[["A","R","E"],["Y","O","U"]], Lazy([function lambda])],[[["I"],["C","A","N","'","T"]], Lazy([function lambda])],[[["I"],["A","M"]], Lazy([function lambda])],[[["I","'","M"]], Lazy([function lambda])],[[["Y","O","U"]], Lazy([function lambda])],[[["Y","E","S"]], Lazy([function lambda])],[[["N","O"]], Lazy([function lambda])],[[["C","O","M","P","U","T","E","R"]], Lazy([function lambda])],[[["C","O","M","P","U","T","E","R","S"]], Lazy([function lambda])],[[["I"],["W","A","N","T"]], Lazy([function lambda])],[[["W","H","A","T"]], Lazy([function lambda])],[[["H","O","W"]], Lazy([function lambda])],[[["W","H","O"]], Lazy([function lambda])],[[["W","H","E","R","E"]], Lazy([function lambda])],[[["W","H","E","N"]], Lazy([function lambda])],[[["N","A","M","E"]], Lazy([function lambda])],[[["W","H","Y"]], Lazy([function lambda])],[[["C","A","U","S","E"]], Lazy([function lambda])],[[["B","E","C","A","U","S","E"]], Lazy([function lambda])],[[["D","R","E","A","M"]], Lazy([function lambda])],[[["S","O","R","R","Y"]], Lazy([function lambda])],[[["H","I"]], Lazy([function lambda])],[[["D","R","E","A","M","S"]], Lazy([function lambda])],[[["M","A","Y","B","E"]], Lazy([function lambda])],[[["H","E","L","L","O"]], Lazy([function lambda])],[[["A","L","W","A","Y","S"]], Lazy([function lambda])],[[["Y","O","U","R"]], Lazy([function lambda])],[[["A","L","I","K","E"]], Lazy([function lambda])],[[["T","H","I","N","K"]], Lazy([function lambda])],[[["F","R","I","E","N","D","S"]], Lazy([function lambda])],[[["F","R","I","E","N","D"]], Lazy([function lambda])],[[], Lazy([function lambda])]], Lazy([function lambda])] fun prefix(xxs, yys) = if xxs is Nil then true diff --git a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls index 0212dc307..81e278242 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls @@ -17,7 +17,7 @@ fun myBind(m, f) = MyState(s => if myRunState(m, s) is [s_, a] then myRunState(f fun myReturn(a) = MyState(s => [s, a]) let myGet = MyState(s => [s, s]) -//│ myGet = MyState([function]) +//│ myGet = MyState([function lambda]) fun myEvalState(m, s) = if myRunState(m, s) is [s_, a] then a @@ -61,7 +61,7 @@ fun eqTerm(a, b) = if a is fun myMaybe(d, f, x) = if x is Some(x) then f(x) let incr = myReturn(Unit) -//│ incr = MyState([function]) +//│ incr = MyState([function lambda]) fun lookupVar(v) = fun lookup2(env) = myMaybe(dummy => throw Error("undefined"), x => x, lookup(v, env)) diff --git a/hkmc2/shared/src/test/mlscript/nofib/life.mls b/hkmc2/shared/src/test/mlscript/nofib/life.mls index 0b99c2424..e93b384e8 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/life.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/life.mls @@ -51,7 +51,7 @@ let start = (lazy of () => LzNil) :: (lazy of () => LzNil) :: lzfy(0 :: 0 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: Nil) :: Nil -//│ start = [Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function tmp])] +//│ start = [Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda])] fun elt(a_b_c, d_e_f, g_h_i) = if a_b_c is [a, b, c] and d_e_f is [d, e, f] and g_h_i is [g, h, i] then let tot = a + b + c + d + f + g + h + i diff --git a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls index c18018b8f..3db4c0efd 100644 --- a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls +++ b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls @@ -36,17 +36,17 @@ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.30: 1 //│ ╙── ^ -//│ = [function block$res5] +//│ = [function lambda] :sjs + //│ JS (unsanitized): -//│ (arg1, arg2) => { return arg1 + arg2 } -//│ = [function block$res6] +//│ let lambda1; lambda1 = function lambda(arg1, arg2) { return arg1 + arg2 }; lambda1 +//│ = [function lambda] * -//│ = [function block$res7] +//│ = [function lambda] :w :pt @@ -58,7 +58,7 @@ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.53: 1 //│ ╙── ^ -//│ = [function block$res8] +//│ = [function lambda] fun (??) foo(x, y) = x + y diff --git a/hkmc2/shared/src/test/mlscript/std/Rendering.mls b/hkmc2/shared/src/test/mlscript/std/Rendering.mls index fbdae341d..7f5182875 100644 --- a/hkmc2/shared/src/test/mlscript/std/Rendering.mls +++ b/hkmc2/shared/src/test/mlscript/std/Rendering.mls @@ -44,7 +44,7 @@ render(Object.create(null)) //│ = "[object]" render(x => x) -//│ = "[function]" +//│ = "[function lambda]" class Foo diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls index 9ca91672f..28bbf8b02 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls @@ -88,8 +88,8 @@ fun //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.82: @inline let //│ ╙── ^^^^^^ -//│ abs = [function abs] -//│ clamp = [function clamp] +//│ abs = [function lambda] +//│ clamp = [function lambda] :w // Only the first variable is annotated with @inline. diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls index e7dbac587..a1f0de6f4 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -9,7 +9,8 @@ class B :sjs x => if x is Pair(A, B) then 1 //│ JS (unsanitized): -//│ (x) => { +//│ let lambda; +//│ lambda = function lambda(x) { //│ let param0, param1; //│ if (x instanceof Pair1.class) { //│ param0 = x.a; @@ -18,16 +19,17 @@ x => if x is Pair(A, B) then 1 //│ if (param1 instanceof B1) { //│ return 1 //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } -//│ } -//│ = [function block$res2] +//│ }; +//│ lambda +//│ = [function lambda] // :e // FIXME: should be an exhaustiveness error From 94a3c0f51ced3fcb9e293c50ed3fc3a4104958d7 Mon Sep 17 00:00:00 2001 From: Mark Ng <55091936+CAG2Mark@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:22:49 +0000 Subject: [PATCH 073/127] fix whitespace --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 119 +++++++++--------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 2ca828fca..18fe27a03 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -118,6 +118,7 @@ object Lifter: Tree.Ident("__Cont")), Tree.Ident("class")) => true case _ => false + /** * Lifts classes and functions to the top-level. Also automatically rewrites lambdas. * Assumes the input block does not have any `HandleBlock`s. @@ -227,7 +228,7 @@ class Lifter(using State, Raise): val varsList = cap.toList val defn = ClsLikeDefn( - None, clsSym, BlockMemberSymbol(nme, Nil), + None, clsSym, BlockMemberSymbol(nme, Nil), syntax.Cls, S(PlainParamList(varsList.map(s => Param(FldFlags.empty, varsMap(s), None)))), Nil, None, Nil, Nil, Nil, End(), End() @@ -239,14 +240,14 @@ class Lifter(using State, Raise): /** * 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. + * @param c The class from which to get the inner symbols. * @return The inner symbols reference within a class. */ - def getInnerSymbols(c: Defn) = + def getInnerSymbols(c: Defn) = val sym = c match case f: FunDefn => f.sym case c: ClsLikeDefn => c.isym - case _ => c.sym // unreachable + case _ => c.sym // unreachable innerSymCache.get(sym) match case Some(value) => value @@ -278,7 +279,7 @@ class Lifter(using State, Raise): !candVars.intersect(captureFnVars).isEmpty /** - * Gets the immutable local variables of a function that need to captured by a definition being lifted. + * Gets the immutable local variables of a function that need to 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. @@ -389,12 +390,12 @@ class Lifter(using State, Raise): val clsCaptures: List[InnerSymbol] = ctx.prevClsDefns.map(_.isym) val refBms = inScopeRefs.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) - if ctx.ignored(d.sym) || - (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then + if ctx.ignored(d.sym) || + (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then d match - case f: FunDefn => + case f: FunDefn => createLiftInfoFn(f, ctx) - case c: ClsLikeDefn => + case c: ClsLikeDefn => createLiftInfoCls(c, ctx) case _ => Map.empty else @@ -416,9 +417,9 @@ class Lifter(using State, Raise): ) d match - case f: FunDefn => + case f: FunDefn => createLiftInfoFn(f, ctx) + (d.sym -> info) - case c: ClsLikeDefn => + case c: ClsLikeDefn => createLiftInfoCls(c, ctx) + (d.sym -> info) case _ => Map.empty @@ -430,7 +431,7 @@ class Lifter(using State, Raise): val defns = c.preCtor.floatOut(ctx)._2 ++ c.ctor.floatOut(ctx)._2 val newCtx = ctx.addClsDefn(c) defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap - ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) + ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and @@ -450,7 +451,7 @@ class Lifter(using State, Raise): val newArgs = args.map(applyArg(_)) Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) case _ => super.applyResult(r) - case c @ Instantiate(InstSel(l), args) => + case c @ Instantiate(InstSel(l), args) => ctx.bmsReqdInfo.get(l) match case Some(info) if !ctx.isModOrObj(l) => val extraArgs = getCallArgs(l, ctx) @@ -458,13 +459,13 @@ class Lifter(using State, Raise): Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) case _ => super.applyResult(r) // if possible, directly create the bms and replace the result with it - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => createCall(l, ctx) case _ => super.applyResult(r) // otherwise, there's no choice but to create the call earlier override def applyPath(p: Path): Path = p match - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => val newSym = syms.get(l) match case None => val newSym = FlowSymbol(l.nme + "$this") @@ -480,10 +481,10 @@ class Lifter(using State, Raise): (walker.applyBlock(b), syms.toList) end rewriteBms - def belongsToCtor(l: Symbol) = + def belongsToCtor(l: Symbol) = ctorCls.match case None => false - case Some(value) => + case Some(value) => value.isym === l // rewrites references to variables @@ -495,7 +496,7 @@ class Lifter(using State, Raise): case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => ctx.getIsymPath(t.owner.get) match - case Some(value) if !belongsToCtor(value) => + case Some(value) if !belongsToCtor(value) => AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) case _ => super.applyBlock(b) @@ -626,7 +627,7 @@ class Lifter(using State, Raise): .replIgnoredBmsPaths(newBmsPaths) d match - case f: FunDefn => + case f: FunDefn => // create second param list with different symbols val extraParamsCpy = extraParams.map(p => p.copy(sym = VarSymbol(p.sym.id))) @@ -641,7 +642,7 @@ class Lifter(using State, Raise): val newDef = FunDefn( base.owner, f.sym, PlainParamList(extraParams) :: f.params, f.body ) - val Lifted(lifted, extras) = liftDefnsInFn(newDef, newCtx) + 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) @@ -693,7 +694,7 @@ class Lifter(using State, Raise): case Some(value) => extraPlist :: value :: auxPlist val fakeCtorDefn = FunDefn( - None, bms, plist, bod + None, bms, plist, bod ) val paramSym2 = paramSyms.getOrElse(Nil) @@ -727,7 +728,7 @@ class Lifter(using State, Raise): case _ => Lifted(d, Nil) - def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = + def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctx) val (ctor, ctorDefns) = c.ctor.floatOut(ctx) @@ -737,7 +738,7 @@ class Lifter(using State, Raise): val modPaths: Map[Local, Local] = ctorIncluded.map: case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) - case _ => S(c.sym -> c.sym) + case _ => S(c.sym -> c.sym) case _ => None .collect: case Some(x) => x @@ -755,15 +756,15 @@ class Lifter(using State, Raise): val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) liftedDefn :: extraDefns - val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) + val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) val methods = fLifted.collect: - case Lifted(liftedDefn, extraDefns) => liftedDefn + case Lifted(liftedDefn, extraDefns) => liftedDefn val fExtra = fLifted.flatMap: case Lifted(liftedDefn, extraDefns) => extraDefns val extras = (ctorDefnsLifted ++ fExtra).map: case f: FunDefn => f.copy(owner = N) - case c: ClsLikeDefn => c.copy(owner = N) + case c: ClsLikeDefn => c.copy(owner = N) case d => d val newDef = c.copy( @@ -784,7 +785,7 @@ class Lifter(using State, Raise): val modPaths: Map[Local, Local] = nested.map: case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) - case _ => S(c.sym -> c.sym) + case _ => S(c.sym -> c.sym) case _ => None .collect: case Some(x) => x @@ -874,7 +875,7 @@ class Lifter(using State, Raise): * 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 * allocation (during class lifting) despite being mutable. - * + * * Assumes the input trees have no lambdas. */ class UsedVarAnalyzer(b: Block)(using State): @@ -915,7 +916,7 @@ class UsedVarAnalyzer(b: Block)(using State): defnsMap += (f.sym -> f) definedLocals += (f.sym -> thisVars) - for d <- thisScopeDefns do + for d <- thisScopeDefns do nestedIn += (d.sym -> f.sym) createMetadataDefn(d, newExisting, newInScope) @@ -927,7 +928,7 @@ class UsedVarAnalyzer(b: Block)(using State): def createMetadataDefn(d: Defn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = d match - case f: FunDefn => + case f: FunDefn => createMetadataFn(f, existing, inScope) case c: ClsLikeDefn => createMetadataCls(c, existing, inScope) @@ -938,7 +939,7 @@ class UsedVarAnalyzer(b: Block)(using State): val thisVars = Lifter.getVars(c) -- existing val newExisting = existing ++ thisVars - val thisScopeDefns: List[Defn] = + val thisScopeDefns: List[Defn] = (c.methods ++ c.preCtor.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2) nestedDefns += c.sym -> thisScopeDefns @@ -950,7 +951,7 @@ class UsedVarAnalyzer(b: Block)(using State): defnsMap += (c.sym -> c) definedLocals += (c.sym -> thisVars) - for d <- thisScopeDefns do + for d <- thisScopeDefns do nestedIn += (d.sym -> c.sym) createMetadataDefn(d, newExisting, newInScope) @@ -962,11 +963,11 @@ class UsedVarAnalyzer(b: Block)(using State): walker.applyBlock(b) DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedIn) - val DefnMetadata(definedLocals, defnsMap, + val DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedIn) = createMetadata private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty - private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = + private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = cacheId.flatMap(blkMutCache.get) match case Some(value) => value case None => @@ -1009,15 +1010,15 @@ class UsedVarAnalyzer(b: Block)(using State): */ private def findAccessesShallow(defn: Defn): AccessInfo = accessedCache.get(defn.sym) match case Some(value) => value - case None => + case None => val ret = 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) => + 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. @@ -1039,10 +1040,10 @@ class UsedVarAnalyzer(b: Block)(using State): defns +:= f; definedVarsDeep ++= definedLocals(f.sym) super.applyFunDefn(f) - override def applyDefn(defn: Defn): Defn = + override def applyDefn(defn: Defn): Defn = defn match case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) - case _ => + case _ => super.applyDefn(defn) walker.applyDefn(d) @@ -1058,8 +1059,8 @@ class UsedVarAnalyzer(b: Block)(using State): val accessInfoMap = accessInfo.toMap - val edges = - for + val edges = + for (sym, AccessInfo(_, _, refd)) <- accessInfo r <- refd if defnSyms.contains(r) @@ -1070,7 +1071,7 @@ class UsedVarAnalyzer(b: Block)(using State): 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 -> + val base = for (id, scc) <- sccs yield id -> scc.foldLeft(AccessInfo.empty): case (acc, sym) => acc ++ accessInfoMap(sym) @@ -1080,7 +1081,7 @@ class UsedVarAnalyzer(b: Block)(using State): case Some(value) => value case None => val ret = sccEdges(scc).foldLeft(base(scc)): - case (acc, nextScc) => acc ++ sccAccessInfo(nextScc) + case (acc, nextScc) => acc ++ sccAccessInfo(nextScc) dp.addOne(scc -> ret) ret @@ -1093,7 +1094,7 @@ class UsedVarAnalyzer(b: Block)(using State): var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = defn match - case _: FunDefn | _: ClsLikeDefn => + case _: FunDefn | _: ClsLikeDefn => accessMap ++= findAccesses(defn); defn case _ => super.applyDefn(defn) walker.applyBlock(b) @@ -1125,12 +1126,12 @@ class UsedVarAnalyzer(b: Block)(using State): hasReader ++= c.hasReader hasMutator ++= c.hasMutator - def rec(blk: Block) = + def rec(blk: Block) = go(blk, reqCapture, hasReader, hasMutator) val walker = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match - case Assign(lhs, rhs, rest) => + case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs applyBlock(rest) @@ -1146,7 +1147,7 @@ class UsedVarAnalyzer(b: Block)(using State): dfltInfo.map(merge) applyBlock(rest) b - case Label(label, body, rest) => + case Label(label, 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). @@ -1186,7 +1187,7 @@ class UsedVarAnalyzer(b: Block)(using State): case _ => () - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) val muts = muted.intersect(thisVars) val reads = defn.freeVars.intersect(thisVars) -- muts // this not a naked reference. if it's a ref to a class, this can only ever create once instance @@ -1198,11 +1199,11 @@ class UsedVarAnalyzer(b: Block)(using State): for l <- reads do if hasMutator.contains(l) then reqCapture += l - hasReader += 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 // many mutators could be created from it - for + for sym <- refd l <- accessMap(sym).mutated do @@ -1221,7 +1222,7 @@ class UsedVarAnalyzer(b: Block)(using State): case _ => super.applyResult(r) override def applyPath(p: Path): Path = p match - case RefOfBms(l) => + case RefOfBms(l) => defnSyms.get(l) match case None => super.applyPath(p) case Some(defn) => @@ -1230,7 +1231,7 @@ class UsedVarAnalyzer(b: Block)(using State): case _ => false if isMod then super.applyPath(p) else - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) val muts = muted.intersect(thisVars) val reads = defn.freeVars.intersect(thisVars) -- muts // this is a naked reference, we assume things it mutates always needs a capture @@ -1240,19 +1241,19 @@ class UsedVarAnalyzer(b: Block)(using State): for l <- reads do if hasMutator.contains(l) then reqCapture += l - hasReader += 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 // many mutators could be created from it - for + for sym <- refd l <- accessMap(sym).mutated do reqCapture += l hasMutator += l - p - case Value.Ref(l) => + p + case Value.Ref(l) => if hasMutator.contains(l) then reqCapture += (l) p case _ => super.applyPath(p) @@ -1302,12 +1303,12 @@ class UsedVarAnalyzer(b: Block)(using State): * @param b * @return */ - def findUsedLocals: Lifter.UsedLocalsMap = + def findUsedLocals: Lifter.UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = usedMap ++= findUsedLocalsDefn(defn) defn - + walker.applyBlock(b) - Lifter.UsedLocalsMap(usedMap) \ No newline at end of file + Lifter.UsedLocalsMap(usedMap) From d8274bcc0a9b18169a2db63049a53926a7fe9852 Mon Sep 17 00:00:00 2001 From: Mark Ng <55091936+CAG2Mark@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:52:48 +0000 Subject: [PATCH 074/127] Fix bug with ctors --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 18fe27a03..540097dc0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -94,6 +94,10 @@ object Lifter: case s: FlowSymbol if !(s is state.runtimeSymbol) => s case _ => Set.empty + def getVarsBlk(b: Block)(using state: State): Set[Local] = + b.definedVars.collect: + case s: FlowSymbol if !(s is state.runtimeSymbol) => s + object RefOfBms: def unapply(p: Path) = p match case Value.Ref(l: BlockMemberSymbol) => S(l) @@ -747,6 +751,7 @@ class Lifter(using State, Raise): val newCtx = ctx .addIsymPath(c.isym, c.isym) .addLocalPaths(modPaths) + .addLocalPaths(getVars(c).map(s => s -> s).toMap) val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) val newCtor = rewriteBlk(ctor, S(c), newCtx) From 34da46a0fc0ab5953ac7887483b0544126b2fe9f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 13:59:00 +0800 Subject: [PATCH 075/127] add broken extends tests --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 31 +++++++++++++++++++ .../src/test/mlscript/lifter/ClassInFun.mls | 20 ++++++++++++ 2 files changed, 51 insertions(+) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 540097dc0..95f2bfd4e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -361,6 +361,37 @@ class Lifter(using State, Raise): case _ => super.applyResult(r) + // don't search within `extends` + override def applyDefn(defn: Defn): Defn = defn match + case defn: FunDefn => applyFunDefn(defn) + case ValDefn(owner, k, sym, rhs) => + val owner2 = owner.mapConserve(_.subst) + val sym2 = sym.subst + val rhs2 = applyPath(rhs) + if (owner2 is owner) && (sym2 is sym) && (rhs2 is rhs) + then defn else ValDefn(owner2, k, sym2, rhs2) + case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentPath, methods, + privateFields, publicFields, preCtor, ctor) => + val own2 = own.mapConserve(_.subst) + val isym2 = isym.subst + val sym2 = sym.subst + val paramsOpt2 = paramsOpt.mapConserve(applyParamList) + val auxParams2 = auxParams.mapConserve(applyParamList) + val methods2 = methods.mapConserve(applyFunDefn) + val privateFields2 = privateFields.mapConserve(_.subst) + val publicFields2 = publicFields.mapConserve(applyTermDefinition) + val preCtor2 = applyBlock(preCtor) + val ctor2 = applyBlock(ctor) + if (own2 is own) && (isym2 is isym) && (sym2 is sym) && + (paramsOpt2 is paramsOpt) && + (auxParams2 is auxParams) && + (methods2 is methods) && + (privateFields2 is privateFields) && + (publicFields2 is publicFields) && + (preCtor2 is preCtor) && (ctor2 is ctor) + then defn else ClsLikeDefn(own2, isym2, sym2, k, paramsOpt2, + auxParams2, parentPath, methods2, privateFields2, publicFields2, preCtor2, ctor2) + override def applyValue(v: Value): Value = v match case RefOfBms(l) if clsSyms.contains(l) && !modOrObj(ctx.defns(l)) => raise(WarningReport( diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 1db1aea6a..cd82d425f 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -522,3 +522,23 @@ fun hello() = class Test2 extends Test 2 //│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null + +:fixme +fun test(x) = + class A with + fun get = x + class B() extends A + B().get +test(2) +//│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' + +:fixme +fun test(x) = + class A with + fun get = x + class B() extends A + 0 is A + B().get +test(2) +//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) From 761e6869ca3af712f478945c50924dfe02f68a88 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 20:49:07 +0800 Subject: [PATCH 076/127] fix many problems with modules, basic extends working for nested classes that don't capture variables --- .../hkmc2/codegen/BlockTransformer.scala | 9 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 320 ++++++++++++------ .../src/test/mlscript/lifter/ClassInFun.mls | 21 ++ .../test/mlscript/lifter/ModulesObjects.mls | 101 +++++- 4 files changed, 341 insertions(+), 110 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 4c8a343cb..bff7b0b10 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -241,14 +241,17 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst // does not traverse into any other block class BlockTransformerNoRec(subst: SymbolSubst) extends BlockTransformerShallow(subst): override def applyBlock(b: Block): Block = b match - case Match(scrut, arms, dflt, rest) => + case Match(scrut, arms, dflt, rest) => val scrut2 = applyPath(scrut) + val arms2 = arms.mapConserve: arm => + val cse2 = applyCase(arm._1) + if (cse2 is arm._1) then arm else (cse2, arm._2) if (scrut is scrut2) then b else Match(scrut2, arms, dflt, rest) - case Assign(lhs, rhs, rest) => + case Assign(lhs, rhs, rest) => applyResult2(rhs): rhs2 => val lhs2 = lhs.subst if (lhs is lhs2) && (rhs is rhs2) then b else Assign(lhs2, rhs2, rest) - case AssignField(lhs, ident, rhs, rest) => + case AssignField(lhs, ident, rhs, rest) => applyResult2(rhs): rhs2 => val lhs2 = applyPath(lhs) if rhs is rhs2 then b else AssignField(lhs2, ident, rhs2, rest)(N) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 95f2bfd4e..22b49df2f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -15,10 +15,6 @@ import scala.collection.mutable.LinkedHashSet import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap import scala.collection.mutable.Set as MutSet -import hkmc2.syntax.Cls -import hkmc2.syntax.Mod -import hkmc2.syntax.Obj -import hkmc2.syntax.Pat object Lifter: /** @@ -141,7 +137,7 @@ class Lifter(using State, Raise): * @param modules The modules in the block to be lifted. * @param localCaptureSyms The symbols in a capture corresponding to a particular local * @param prevFnLocals Locals belonging to function definitions that have already been traversed - * @param prevClsDefns Class definitions that have already been traversed + * @param prevClsDefns Class definitions that have already been traversed, excluding modules * @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) @@ -150,6 +146,7 @@ class Lifter(using State, Raise): */ case class LifterCtx( val defns: Map[BlockMemberSymbol, Defn] = Map.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, @@ -181,8 +178,9 @@ class Lifter(using State, Raise): def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) - def addModules(mods: Set[BlockMemberSymbol]) = copy(modulesObjs = mods ++ modulesObjs) + def addModulesObjs(mods: Set[BlockMemberSymbol]) = copy(modulesObjs = mods ++ modulesObjs) def withDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(defns = mp) + 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 addFnLocals(f: FreeVars) = copy(prevFnLocals = prevFnLocals ++ f) @@ -199,6 +197,7 @@ class Lifter(using State, Raise): def addLocalPath(target: Local, path: Local) = copy(localPaths = localPaths + (target -> path)) def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) + def addIsymPaths(mp: Map[InnerSymbol, Local]) = copy(isymPaths = isymPaths ++ mp) object LifterCtx: def empty = LifterCtx() @@ -312,45 +311,66 @@ class Lifter(using State, Raise): ) // d is a top-level definition - // returns (unliftable classes, modules) - def createMetadata(d: Defn, ctx: LifterCtx): (Set[BlockMemberSymbol], Set[BlockMemberSymbol]) = + // returns (unliftable classes, modules, objects) + def createMetadata(d: Defn, ctx: LifterCtx): (Set[BlockMemberSymbol], List[ClsLikeDefn], List[ClsLikeDefn]) = + var unliftable: Set[BlockMemberSymbol] = Set.empty var clsSymToBms: Map[Local, BlockMemberSymbol] = Map.empty - var modules: Set[BlockMemberSymbol] = Set.empty - + var modules: List[ClsLikeDefn] = Nil + var objects: List[ClsLikeDefn] = Nil + + d match + case c @ ClsLikeDefn(k = syntax.Mod) => modules +:= c + case c @ ClsLikeDefn(k = syntax.Obj) => objects +:= c + case _ => () + + // search for modules val walker = new BlockTransformer(SymbolSubst()): override def applyDefn(defn: Defn): Defn = if defn === d then super.applyDefn(defn) else defn match - case c: ClsLikeDefn => + case c: ClsLikeDefn => clsSymToBms += c.isym -> c.sym - if modOrObj(c) then modules += c.sym + if c.k is syntax.Mod then raise(WarningReport( msg"Modules are not yet properly lifted and will break." -> N :: Nil, N, Diagnostic.Source.Compilation )) + modules +:= c + else if c.k is syntax.Obj then + objects +:= c case _ => () super.applyDefn(defn) - walker.applyDefn(d) + // search for defns nested within a top-level module, which can always be lifted + 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 + + val isMod = d match + case c: ClsLikeDefn if c.k is syntax.Mod => true + case _ => false + + val liftable = if isMod then inModuleDefns(d) else Set.empty + val clsSyms = clsSymToBms.values.toSet - - var unliftable: Set[BlockMemberSymbol] = Set.empty val walker2 = new BlockTransformer(SymbolSubst()): - override def applyCase(cse: Case): Case = + override def applyCase(cse: Case): Case = cse match - case Case.Cls(cls, path) => clsSymToBms.get(cls) match - case None => () - case Some(value) => + case Case.Cls(cls, path) => + clsSymToBms.get(cls) match + case Some(value) if !liftable.contains(value) => raise(WarningReport( msg"Cannot yet lift the class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation )) unliftable += value + case _ => () case _ => () cse - + override def applyResult(r: Result): Result = r match case Call(Value.Ref(_: BlockMemberSymbol), args) => args.map(applyArg) @@ -358,10 +378,10 @@ class Lifter(using State, Raise): case Instantiate(InstSel(_), args) => args.map(applyPath) r - + case _ => super.applyResult(r) - // don't search within `extends` + // don't search within `extends`, otherwise it'll think it's used as a first-class class override def applyDefn(defn: Defn): Defn = defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(owner, k, sym, rhs) => @@ -402,8 +422,8 @@ class Lifter(using State, Raise): v case _ => super.applyValue(v) walker2.applyDefn(d) - - (unliftable, modules) + + (unliftable, modules, objects) extension (b: Block) private def floatOut(ctx: LifterCtx) = @@ -436,8 +456,8 @@ class Lifter(using State, Raise): else val modLocal = d match case c: ClsLikeDefn if modOrObj(c) => parentCls match - case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) - case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) + case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) + case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) case _ => N val fakeCtorBms = d match @@ -459,19 +479,105 @@ class Lifter(using State, Raise): case _ => Map.empty def createLiftInfoFn(f: FunDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = - val (_, defns) = f.body.floatOut(ctx) + 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.floatOut(ctx)._2 ++ c.ctor.floatOut(ctx)._2 - val newCtx = ctx.addClsDefn(c) + val newCtx = if c.k is syntax.Mod then ctx else ctx.addClsDefn(c) defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) + def refRewriter(ctx: LifterCtx, ctorCls: Opt[ClsLikeDefn]) = + def belongsToCtor(l: Symbol) = + ctorCls match + case None => false + case Some(value) => + value.isym === l + + new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match + case Some(value) if !belongsToCtor(lhs) => + Assign(value, applyResult(rhs), applyBlock(rest)) + case _ => super.applyBlock(b) + + case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(t.owner.get) => + AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) + case _ => super.applyBlock(b) + + case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match + case Some(captureSym) => + AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) + case None => ctx.getLocalPath(lhs) match + case None => super.applyBlock(b) + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + + case Define(d: Defn, rest: Block) => ctx.getBmsReqdInfo(d.sym) match + case Some(LiftedInfo(modLocal = S(sym))) => + blockBuilder + .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) + .rest(applyBlock(rest)) + case _ => super.applyBlock(b) + + case _ => super.applyBlock(b) + + override def applyPath(p: Path): Path = + p match + // These two cases rewrites `this.whatever` when referencing an outer class's fields. + case Value.Ref(l: InnerSymbol) => + ctx.getIsymPath(l) match + case Some(value) if !belongsToCtor(l) => Value.Ref(value) + case _ => super.applyPath(p) + case Value.Ref(t: TermSymbol) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(t.owner.get) => Select(value.asPath, t.id)(N) + case _ => super.applyPath(p) + + // Rewrites this.className.class to reference the top-level definition + case s @ Select(RefOfBms(l), Tree.Ident("class")) if !ctx.ignored(l) => + // this class will be lifted, rewrite the ref to strip it of `Select` + Select(Value.Ref(l), Tree.Ident("class"))(s.symbol) + + // 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(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) + case _ => super.applyPath(p) + + // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol + // reference is passed as function parameters). + case RefOfBms(l) if ctx.ignored(l) => ctx.getIgnoredBmsPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + + // 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) => + Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) + case None => ctx.getLocalPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + case _ => super.applyPath(p) + + override def applyDefn(defn: Defn): Defn = defn match + case c: ClsLikeDefn => + val par2 = c.parentPath.mapConserve(applyPath) + if par2 is c.parentPath then defn + else c.copy(parentPath = par2) + case _ => super.applyDefn(defn) + + def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, // it also directly rewrites Results. + def rewriteBms(b: Block, ctx: LifterCtx) = val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty @@ -508,76 +614,20 @@ class Lifter(using State, Raise): newSym case Some(value) => value Value.Ref(newSym) - case RefOfBms(l) => ctx.getIgnoredBmsPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) - case _ => super.applyPath(p) (walker.applyBlock(b), syms.toList) end rewriteBms - - def belongsToCtor(l: Symbol) = - ctorCls.match - case None => false - case Some(value) => - value.isym === l + // rewrites references to variables - val transformer1 = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match - case Some(value) if !belongsToCtor(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - case _ => super.applyBlock(b) - - case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => - ctx.getIsymPath(t.owner.get) match - case Some(value) if !belongsToCtor(value) => - AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) - case _ => super.applyBlock(b) - - case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match - case Some(captureSym) => - AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) - case None => ctx.getLocalPath(lhs) match - case None => super.applyBlock(b) - case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - - case Define(d: Defn, rest: Block) => ctx.getBmsReqdInfo(d.sym) match - case Some(LiftedInfo(modLocal = S(sym))) => - blockBuilder - .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) - .rest(applyBlock(rest)) - case _ => super.applyBlock(b) - - - case _ => super.applyBlock(b) - - override def applyPath(p: Path): Path = p match - case Value.Ref(l: InnerSymbol) => ctx.getIsymPath(l) match - case Some(value) if !belongsToCtor(value) => Value.Ref(value) - case _ => super.applyPath(p) - case Value.Ref(t: TermSymbol) if t.owner.isDefined => - ctx.getIsymPath(t.owner.get) match - case Some(value) if !belongsToCtor(value) => Select(value.asPath, t.id)(N) - case _ => super.applyPath(p) - case s @ Select(qual, ident) => - s.symbol.flatMap(ctx.getLocalPath) match - case Some(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) - case _ => super.applyPath(p) - case Value.Ref(l) => ctx.getLocalCaptureSym(l) match - case Some(captureSym) => - Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) - case None => ctx.getLocalPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) - case _ => super.applyPath(p) + val transformer1 = refRewriter(ctx, ctorCls) // rewrites references to block member symbols val transformer2 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = val (rewriten, syms) = rewriteBms(b, ctx) val pre = syms.foldLeft(blockBuilder): - case (blk, (bms, local)) => + case (blk, (bms, local)) => val initial = blk.assign(local, createCall(bms, ctx)) ctx.defns(bms) match case c: ClsLikeDefn => initial.assignFieldN(local.asPath, Tree.Ident("class"), bms.asPath) @@ -751,8 +801,8 @@ class Lifter(using State, Raise): case c: ClsLikeDefn if modOrObj(c) => // module or object // force it to be a class val newK = c.k match - case Mod => syntax.Mod - case Obj => syntax.Cls + case syntax.Mod => syntax.Mod + case syntax.Obj => syntax.Cls case _ => c.k // unreachable val newDef = c.copy( @@ -770,7 +820,17 @@ class Lifter(using State, Raise): val allCtorDefns = preCtorDefns ++ ctorDefns val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) - val modPaths: Map[Local, Local] = ctorIncluded.map: + // if this is a module, add a method to reference the classes lifted out + val extraMethods = if c.k is syntax.Mod then ctorIncluded.collect: + case cls: ClsLikeDefn if cls.k is syntax.Cls => + FunDefn( + S(c.isym), + BlockMemberSymbol(cls.sym.nme, Nil), + Nil, + Return(cls.sym.asPath, false) + ) + else Nil + val nestedClsPaths: Map[Local, Local] = ctorIncluded.map: case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) @@ -779,15 +839,17 @@ class Lifter(using State, Raise): case Some(x) => x .toMap - val newCtx = ctx - .addIsymPath(c.isym, c.isym) - .addLocalPaths(modPaths) + val newCtx_ = ctx + .addLocalPaths(nestedClsPaths) .addLocalPaths(getVars(c).map(s => s -> s).toMap) + val newCtx = c.k match + case syntax.Mod => newCtx_ + case _ => newCtx_.addIsymPath(c.isym, c.isym) + val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) val newCtor = rewriteBlk(ctor, S(c), newCtx) - val ctorDefnsLifted = ctorIncluded.flatMap: defn => val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) liftedDefn :: extraDefns @@ -803,10 +865,22 @@ class Lifter(using State, Raise): case c: ClsLikeDefn => c.copy(owner = N) case d => d + def rewriteExtends(p: Path): Path = p match + case RefOfBms(b) => + // we may need to add `class` in case the lifting added extra params + if ctx.getBmsReqdInfo(b).isDefined then Select(b.asPath, Tree.Ident("class"))(N) + else b.asPath + case Select(RefOfBms(b), Tree.Ident("class")) => Select(b.asPath, Tree.Ident("class"))(N) + case _ => return p + + // if this class extends something, rewrite + val newPar = c.parentPath.map(rewriteExtends) + val newDef = c.copy( - methods = methods, + methods = extraMethods ++ methods, preCtor = newPreCtor, - ctor = newCtor + parentPath = newPar, + ctor = newCtor, ) Lifted(newDef, extras) @@ -885,6 +959,26 @@ class Lifter(using State, Raise): super.applyBlock(ret) transformer.applyBlock(b) + // lift things out of top-level modules. this is easy + def liftOutTopLevelMod(c: ClsLikeDefn, ctx: LifterCtx): Lifted[Defn] = + val clsDefns = ctx.nestedDefns(c.sym).collect: + case c: ClsLikeDefn => c + val isymMap = clsDefns.map: c => + c.isym -> c.sym + .toMap + val bmsMap = clsDefns.map: c => + c.sym -> BlockMemberSymbol(c.sym.nme, Nil) + .toMap + + val newDefs = bmsMap.toList.map: + case bms -> bms2 => ValDefn(S(c.isym), syntax.ImmutVal, bms2, bms.asPath) + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyBlock(b: Block): Block = b match + case Define(defn, rest) => applyBlock(rest) + case _ => super.applyBlock(b) + + ??? + // top-level def transform(b: Block) = val blk = desugarLambdas(b) @@ -892,20 +986,41 @@ class Lifter(using State, Raise): val ctx = LifterCtx .withLocals(analyzer.findUsedLocals) .withDefns(analyzer.defnsMap) + .withNestedDefns(analyzer.nestedDefns) .withAccesses(analyzer.accessMap) .withInScopes(analyzer.inScopeDefns) + + var unliftable: Set[BlockMemberSymbol] = Set.empty + var modules: List[ClsLikeDefn] = List.empty + var objects: List[ClsLikeDefn] = List.empty + + val walker = new BlockTransformerShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Defn = + val (unl, mod, obj) = createMetadata(defn, ctx) + unliftable ++= unl + modules ++= mod + objects ++= obj + defn + + walker.applyBlock(b) + + val ctxx = ctx + .addIgnored(unliftable) + .addModulesObjs((modules ++ objects).map(_.sym).toSet) + .addIsymPaths(modules.map(m => m.isym -> m.sym).toMap) + + val walker1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => - val (unliftable, modules) = createMetadata(d, ctx) - val ctxx = ctx.addIgnored(unliftable).addModules(modules) + val Lifted(lifted, extra) = d match case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, ctxx))) case c: ClsLikeDefn => liftDefnsInCls(c, ctxx.addBmsReqdInfo(createLiftInfoCls(c, ctxx))) case _ => return super.applyBlock(b) (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) - walker.applyBlock(blk) + walker1.applyBlock(blk) /** * Analyzes which variables have been used and mutated by which functions. @@ -1001,6 +1116,10 @@ class UsedVarAnalyzer(b: Block)(using State): val DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedIn) = createMetadata + + def isModule(s: BlockMemberSymbol) = defnsMap.get(s) match + case S(c: ClsLikeDefn) => c.k is syntax.Mod + case _ => false private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -1022,7 +1141,8 @@ class UsedVarAnalyzer(b: Block)(using State): override def applyValue(v: Value): Value = v match case Value.Ref(_: BuiltinSymbol) => super.applyValue(v) case RefOfBms(l) => - accessed = accessed.addRefdDefn(l); v + if !isModule(l) then accessed = accessed.addRefdDefn(l) + v case Value.Ref(l) => accessed = accessed.addAccess(l); v case _ => super.applyValue(v) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index cd82d425f..22cab0724 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -532,7 +532,28 @@ fun test(x) = test(2) //│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' +:expect 2 +fun test() = + class A with + fun get = 2 + class B() extends A + B().get +test() +//│ = 2 + :fixme +:expect 2 +fun test(x) = + class A with + fun get = x + class B() extends A + B().get +test(2) +//│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + +// will be easier with when `let class` is added +:todo fun test(x) = class A with fun get = x diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index c98cd5d30..eed838ef1 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -34,9 +34,7 @@ fun foo(x, y) = foo //│ ═══[WARNING] Modules are not yet properly lifted and will break. //│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let foo1, M3, foo2, foo$, foo$capture1;try { foo$ = function foo$(...args) { globalThis.Predef.checkArgs("foo$", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture2 = args[2]; return runtime.checkCall(M4.foo()) }; foo1 = function foo(...args) { globalThis.Predef.checkArgs("foo", 3, true, args.length); let M4 = args[0]; let x = args[1]; let foo$capture2 = args[2]; return (...args1) => { globalThis.Predef.checkArgs("", 0, true, args1.length); return runtime.checkCall(foo$(M4, x, foo$capture2)) } }; M3 = class M2 { static { return (x$1, foo$capture$0) => { this.x$1 = x$1; this.foo$capture$0 = foo$capture$0; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); M2.foo$capture$0.y0$ = 2; return M2.x$1 + M2.foo$capture$0.y0$ } static toString() { return "M"; } }; foo$capture1 = function foo$capture(...args) { return new foo$capture.class(...args); }; foo$capture1.class = class foo$capture { constructor(y0$) { this.y0$ = y0$; } toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let tmp, M$, capture; capture = new foo$capture1(y); M$ = runtime.checkCall(M3(x, capture)); tmp = runtime.checkCall(foo$(M$, x, capture)); return tmp }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: M$ (class hkmc2.semantics.VarSymbol) :fixme :expect 12 @@ -77,14 +75,57 @@ fun foo(x, y) = foo(10, 0) //│ = 14 -:todo +:sjs fun foo(x, y) = object M with - fun foo() = + fun foo2() = set y = 2 x + y - fun foo = M.foo() - foo + fun foo3 = M.foo2() + foo3 +//│ JS (unsanitized): +//│ let M9, foo31, foo4, foo3$, foo$capture1; +//│ foo3$ = function foo3$(M10, x, foo$capture2) { +//│ return M10.foo2() +//│ }; +//│ foo31 = function foo3(M10, x, foo$capture2) { +//│ return () => { +//│ return foo3$(M10, x, foo$capture2) +//│ } +//│ }; +//│ M9 = function M(x$11, foo$capture$01) { +//│ return new M.class()(x$11, foo$capture$01); +//│ }; +//│ M9.class = class M8 { +//│ constructor() { +//│ return (x$1, foo$capture$0) => { +//│ this.x$1 = x$1; +//│ this.foo$capture$0 = foo$capture$0; +//│ return this; +//│ } +//│ } +//│ foo2() { +//│ this.foo$capture$0.y0$ = 2; +//│ return this.x$1 + this.foo$capture$0.y0$ +//│ } +//│ toString() { return "M"; } +//│ }; +//│ foo$capture1 = function foo$capture(y0$1) { +//│ return new foo$capture.class(y0$1); +//│ }; +//│ foo$capture1.class = class foo$capture { +//│ constructor(y0$) { +//│ this.y0$ = y0$; +//│ } +//│ toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } +//│ }; +//│ foo4 = function foo(x, y) { +//│ let tmp1, M$, capture; +//│ capture = new foo$capture1(y); +//│ M$ = M9(x, capture); +//│ tmp1 = foo3$(M$, x, capture); +//│ return tmp1 +//│ }; :expect 12 foo(10, 0) @@ -168,3 +209,49 @@ object M with g M.f()() //│ = 2 + +:sjs +module M with + class A() with + fun get = M.x + x + val x = if A() is A then 2 else 3 +M.A().get +//│ JS (unsanitized): +//│ let A9, M21, tmp4; +//│ A9 = function A() { +//│ return new A.class(); +//│ }; +//│ A9.class = class A8 { +//│ constructor() {} +//│ get get() { +//│ return M21.x + M21.x; +//│ } +//│ toString() { return "A(" + "" + ")"; } +//│ }; +//│ M21 = class M20 { +//│ static { +//│ let scrut, tmp5; +//│ scrut = M20.A(); +//│ if (scrut instanceof A9.class) { +//│ tmp5 = 2; +//│ } else { +//│ tmp5 = 3; +//│ } +//│ this.x = tmp5; +//│ } +//│ static get A() { +//│ return A9; +//│ } +//│ static toString() { return "M"; } +//│ }; +//│ tmp4 = M21.A(); +//│ tmp4.get +//│ = 4 + +module M with + val x = 2 + class A with + fun get = x + class B() extends A +M.B().get +//│ = 2 From 26ea02cac9fe21ce3e08c5eec199d57138038759 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 22:43:30 +0800 Subject: [PATCH 077/127] Prepare for PR merge --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 70 ++++---- .../src/test/mlscript/backlog/Lifter.mls | 149 ++++++++++++++++++ .../src/test/mlscript/lifter/ClassInFun.mls | 13 +- .../test/mlscript/lifter/ModulesObjects.mls | 2 - 4 files changed, 193 insertions(+), 41 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/backlog/Lifter.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 22b49df2f..4c67c9f7d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -130,11 +130,12 @@ class Lifter(using State, Raise): * The context of the class lifter. One can create an empty context using `Lifter.empty`. * * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested definitions. + * @param nestedDefns Definitions which are nested in a given definition (shallow). * @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 modules The modules in the block to be lifted. + * @param modLocals 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 * @param prevFnLocals Locals belonging to function definitions that have already been traversed * @param prevClsDefns Class definitions that have already been traversed, excluding modules @@ -151,7 +152,7 @@ class Lifter(using State, Raise): val accessInfo: Map[BlockMemberSymbol, AccessInfo] = Map.empty, val ignoredDefns: Set[BlockMemberSymbol] = Set.empty, val inScopeDefns: Map[BlockMemberSymbol, Set[BlockMemberSymbol]] = Map.empty, - val modulesObjs: Set[BlockMemberSymbol] = Set.empty, + val modLocals: Map[BlockMemberSymbol, Local] = Map.empty, val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol] = Map.empty, val prevFnLocals: FreeVars = FreeVars.empty, val prevClsDefns: List[ClsLikeDefn] = Nil, @@ -163,22 +164,19 @@ class Lifter(using State, Raise): ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) - // the path to access the capture of a particular function + def getCapturePath(b: BlockMemberSymbol) = capturePaths.get(b) - // the path to access the capture of the function that a local belongs to def getLocalClosPath(l: Local) = lookup(l).flatMap(capturePaths.get(_)) - // the symbol in the capture corresponding to a particular local def getLocalCaptureSym(l: Local) = localCaptureSyms.get(l) - // how to access a variable in the local scope def getLocalPath(l: Local) = localPaths.get(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) = modulesObjs.contains(b) + def isModOrObj(b: BlockMemberSymbol) = modLocals.contains(b) def getAccesses(sym: BlockMemberSymbol) = accessInfo(sym) def addIgnored(defns: Set[BlockMemberSymbol]) = copy(ignoredDefns = ignoredDefns ++ defns) - def addModulesObjs(mods: Set[BlockMemberSymbol]) = copy(modulesObjs = mods ++ modulesObjs) + def withModLocals(mp: Map[BlockMemberSymbol, Local]) = copy(modLocals = modLocals ++ mp) def withDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(defns = mp) def withNestedDefns(mp: Map[BlockMemberSymbol, List[Defn]]) = copy(nestedDefns = mp) def withAccesses(mp: Map[BlockMemberSymbol, AccessInfo]) = copy(accessInfo = mp) @@ -302,7 +300,6 @@ class Lifter(using State, Raise): val reqdBms: List[BlockMemberSymbol], // pass ignored blockmembersymbols val fakeCtorBms: Option[BlockMemberSymbol], // only for classes val singleCallBms: BlockMemberSymbol, // optimization - val modLocal: Opt[Local] // for modules ) case class Lifted[+T <: Defn]( @@ -440,11 +437,17 @@ class Lifter(using State, Raise): .map(sym => ctx.lookup(sym).get) .toList.sortBy(_.uid) - val refMod = inScopeRefs.intersect(ctx.modulesObjs) + val refMod = inScopeRefs.intersect(ctx.modLocals.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 modLocal = d match + case c: ClsLikeDefn if modOrObj(c) => parentCls match + case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) + case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) + case _ => N + if ctx.ignored(d.sym) || (includedCaptures.isEmpty && includedLocals.isEmpty && clsCaptures.isEmpty && refBms.isEmpty) then d match @@ -454,12 +457,6 @@ class Lifter(using State, Raise): createLiftInfoCls(c, ctx) case _ => Map.empty else - val modLocal = d match - case c: ClsLikeDefn if modOrObj(c) => parentCls match - case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) - case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) - case _ => N - val fakeCtorBms = d match case c: ClsLikeDefn if !modLocal.isDefined => S(BlockMemberSymbol(d.sym.nme + "$ctor", Nil)) case _ => N @@ -468,7 +465,7 @@ class Lifter(using State, Raise): val info = LiftedInfo( includedCaptures, includedLocals, clsCaptures, - refBms, fakeCtorBms, singleCallBms, modLocal + refBms, fakeCtorBms, singleCallBms ) d match @@ -515,11 +512,16 @@ class Lifter(using State, Raise): case None => super.applyBlock(b) case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - case Define(d: Defn, rest: Block) => ctx.getBmsReqdInfo(d.sym) match - case Some(LiftedInfo(modLocal = S(sym))) => - blockBuilder - .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) - .rest(applyBlock(rest)) + case Define(d: Defn, rest: Block) => ctx.modLocals.get(d.sym) match + case Some(sym) => ctx.getBmsReqdInfo(d.sym) match + case Some(_) => + blockBuilder + .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) + .rest(applyBlock(rest)) + case None => + blockBuilder + .assign(sym, Call(d.sym.asPath, Nil)(true, false)) + .rest(applyBlock(rest)) case _ => super.applyBlock(b) case _ => super.applyBlock(b) @@ -657,7 +659,7 @@ class Lifter(using State, Raise): case f: FunDefn => liftDefnsInFn(f, ctx) case c: ClsLikeDefn => liftDefnsInCls(c, ctx) case _ => Lifted(d, Nil) - case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms, modLocal)) => + case S(LiftedInfo(includedCaptures, includedLocals, clsCaptures, reqdBms, fakeCtorBms, singleCallBms)) => val createSym = d match case d: ClsLikeDefn => // due to the possibility of capturing a TempSymbol in HandlerLowering, it is necessary to generate a discriminator @@ -831,8 +833,8 @@ class Lifter(using State, Raise): ) else Nil val nestedClsPaths: Map[Local, Local] = ctorIncluded.map: - case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match - case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) + case c: ClsLikeDefn if modOrObj(c) => ctx.modLocals.get(c.sym) match + case Some(sym) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) case _ => None .collect: @@ -893,8 +895,8 @@ class Lifter(using State, Raise): val (ignored, included) = nested.partition(d => ctx.ignored(d.sym)) val modPaths: Map[Local, Local] = nested.map: - case c: ClsLikeDefn if modOrObj(c) => ctx.getBmsReqdInfo(c.sym) match - case Some(LiftedInfo(modLocal = Some(sym))) => S(c.sym -> sym) + case c: ClsLikeDefn if modOrObj(c) => ctx.modLocals.get(c.sym) match + case Some(sym) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) case _ => None .collect: @@ -1005,9 +1007,21 @@ class Lifter(using State, Raise): walker.applyBlock(b) + val modLocals = (modules ++ objects).map: c => + analyzer.nestedIn.get(c.sym) match + case Some(bms) => + val nestedIn = analyzer.defnsMap(bms) + nestedIn match + 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 + val ctxx = ctx .addIgnored(unliftable) - .addModulesObjs((modules ++ objects).map(_.sym).toSet) + .withModLocals(modLocals) .addIsymPaths(modules.map(m => m.isym -> m.sym).toMap) val walker1 = new BlockTransformerShallow(SymbolSubst()): diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls new file mode 100644 index 000000000..7a45bc7ea --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -0,0 +1,149 @@ +:js +:lift +:todo + +// We use the optional `symbol` parameter of `Select` to detect references to the +// BlockMemberSymbol. But when this symbol is not present, there is no way to properly +// detect it. What do to? +:expect 3 +class A(x) with + class B(y) with + fun getB() = x + y + fun getA() = this.B(2).getB() +A(1).getA() +//│ ═══[RUNTIME ERROR] TypeError: this.B is not a function +//│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' + +// This is due to the order of classes after lifting +class Test +fun hello() = + class Test2 extends Test + 2 +//│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null + +// This is due to subclasses not calling `super` with the required locals. The analysis to +// determine which locals are required is in-place yet. +:expect 2 +fun test(x) = + class A with + fun get = x + class B() extends A + B().get +test(2) +//│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + +/// The following are related to first-class classes and instance checks. /// + +:w +:expect 1 +fun f(used1, unused1) = + fun g(g_arg) = + let used3 = 2 + fun h = used3 + used1 + h + let unused2 = 2 + class Test(a) with + fun get() = used1 + let foo = Test + foo(unused1) +f(1, 2).get() +//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ = 1 + + +:todo +:w +fun foo(x, n) = + class C() + let res = if x is C then "Y" else "N" + if n > 0 then res + foo(C(), n - 1) else "" +//│ ═══[WARNING] Cannot yet lift the class/module `C` as it is used in an instance check. + +:todo +:w +fun foo() = + class C() + C +//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. + +:todo +:expect "NN" +foo(0, 2) +//│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 +//│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' + +:todo +fun test(x) = + class A with + fun get = x + class B() extends A + 0 is A + B().get +test(2) +//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) + +/// The following are related to modules and objects. /// + +:todo +fun foo(x, y) = + module M with + val test = 2 + fun foo() = + set y = 2 + x + y + test + M.foo() +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let M1, foo2;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp2; M.y$1 = 2; tmp2 = M.x$0 + M.y$1; return tmp2 + M.test } static toString() { return "M"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res9 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement + +:expect 14 +foo(10, 0) +//│ ═══[RUNTIME ERROR] ReferenceError: foo2 is not defined +//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' + +fun foo(x, y) = + module M with + fun foo() = + set y = 2 + x + y + fun foo = M.foo() + foo +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: M$ (class hkmc2.semantics.VarSymbol) + +:expect 12 +foo(10, 0) +//│ ═══[RUNTIME ERROR] ReferenceError: foo4 is not defined +//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' + + +class A(x) with + module M with + fun getB() = x + fun getA() = M.getB() +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ ═══[WARNING] Modules are not yet properly lifted and will break. +//│ > let M5, A6;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A6 = function A(...args1) { return new A.class(...args1); }; A6.class = class A5 { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res13 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > ^^^^^^ +//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement + +:expect 2 +A(2).getA() +//│ ═══[RUNTIME ERROR] ReferenceError: A6 is not defined +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' + +fun foo(x) = + object Foo with + fun self1 = this + fun self2 = Foo + class Bar extends Foo + new Bar.self2 +foo(2) +//│ ═══[WARNING] Cannot yet lift the class `Bar` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift the class `Bar` as it is used as a first-class class. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: Foo$ (class hkmc2.semantics.VarSymbol) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 22cab0724..61af2958a 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -524,6 +524,7 @@ fun hello() = //│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null :fixme +:expect 2 fun test(x) = class A with fun get = x @@ -531,6 +532,7 @@ fun test(x) = B().get test(2) //│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' :expect 2 fun test() = @@ -541,17 +543,6 @@ fun test() = test() //│ = 2 -:fixme -:expect 2 -fun test(x) = - class A with - fun get = x - class B() extends A - B().get -test(2) -//│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' - // will be easier with when `let class` is added :todo fun test(x) = diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index eed838ef1..bd0e84317 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -61,7 +61,6 @@ A(2).getA() //│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' -:todo fun foo(x, y) = object M with val test = 2 @@ -70,7 +69,6 @@ fun foo(x, y) = x + y + test M.foo() -:todo :expect 14 foo(10, 0) //│ = 14 From 52f884461356755e6462752bf58f7292d7648362 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 22:48:25 +0800 Subject: [PATCH 078/127] merge and update tests --- .../src/main/scala/hkmc2/codegen/Lowering.scala | 11 ++++++----- .../src/test/mlscript/codegen/CaseShorthand.mls | 2 +- hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 7986dc1a2..ed772f068 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -613,15 +613,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val stackSafe = config.stackSafety match case N => res case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) - - MergeMatchArmTransformer.applyBlock( + val hdlr = if lowerHandlers then HandlerLowering().translateTopLevel(stackSafe) else stackSafe - if lift then Lifter().transform(hdlr) - else hdlr - ) + val lifted = + if lift then Lifter().transform(hdlr) + else hdlr + + MergeMatchArmTransformer.applyBlock(lifted) def program(main: st): Program = def go(acc: Ls[Local -> Str], trm: st): Program = diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index 8796cc618..f93a3a2e6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -89,7 +89,7 @@ val isDefined = case //│ } else if (caseScrut instanceof None1) { //│ return false //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp5 = lambda11; diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index c12dd83a6..47470b5ef 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -42,7 +42,7 @@ val isDefined = case //│ } else if (caseScrut instanceof None1.class) { //│ return false //│ } else { -//│ throw new this.Error("match error"); +//│ throw new globalThis.Error("match error"); //│ } //│ }; //│ tmp1 = lambda; From 70f673c3a5410ca3d8f410b873ae1ec04109c7ff Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 22:52:08 +0800 Subject: [PATCH 079/127] pr changes --- .../scala/hkmc2/codegen/HandlerLowering.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 6369879ac..6e29bb14c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -407,7 +407,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val body = blockBuilder .define(clsDefn) - .assign(h.lhs, Call(clsDefn.sym.asPath, Nil)(true, false)) + .assign(h.lhs, PureCall(clsDefn.sym.asPath, Nil)) .rest(handlerBody) val defn = FunDefn( diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 4c67c9f7d..ed2899940 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -961,26 +961,6 @@ class Lifter(using State, Raise): super.applyBlock(ret) transformer.applyBlock(b) - // lift things out of top-level modules. this is easy - def liftOutTopLevelMod(c: ClsLikeDefn, ctx: LifterCtx): Lifted[Defn] = - val clsDefns = ctx.nestedDefns(c.sym).collect: - case c: ClsLikeDefn => c - val isymMap = clsDefns.map: c => - c.isym -> c.sym - .toMap - val bmsMap = clsDefns.map: c => - c.sym -> BlockMemberSymbol(c.sym.nme, Nil) - .toMap - - val newDefs = bmsMap.toList.map: - case bms -> bms2 => ValDefn(S(c.isym), syntax.ImmutVal, bms2, bms.asPath) - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Define(defn, rest) => applyBlock(rest) - case _ => super.applyBlock(b) - - ??? - // top-level def transform(b: Block) = val blk = desugarLambdas(b) From 50f3de6e39e331b4ad3599b6ff09ac4a5c07100d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 23:04:07 +0800 Subject: [PATCH 080/127] add comments and unused stuff --- .../shared/src/main/scala/hkmc2/codegen/Lifter.scala | 3 +++ .../src/main/scala/hkmc2/codegen/js/JSBuilder.scala | 7 ------- hkmc2/shared/src/test/mlscript/codegen/BadNew.mls | 11 +---------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index ed2899940..c78e0428b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -963,7 +963,10 @@ class Lifter(using State, Raise): // top-level def transform(b: 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 = desugarLambdas(b) + val analyzer = UsedVarAnalyzer(blk) val ctx = LifterCtx .withLocals(analyzer.findUsedLocals) 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 ade8b6429..678ea45f8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -296,13 +296,6 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: S(doc"function ${sym.nme}($ps) ${ funBodRet }") case Nil => N - /* - val fun = paramsOpt match - case S(params) => - val (ps, bod) = setupFunction(some(sym.nme), params, End()) - S(doc"function ${sym.nme}($ps) { return new ${sym.nme}.class($ps); }") - case N => N - */ ownr match case S(owner) => val ths = mkThis(owner) diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index ee9ced41c..5176135c5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -11,15 +11,6 @@ new new 2 //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor -// TODO: somehow desugaring lambdas fixed this, is this intended? -:todo -:sjs -:ge -new 2 + 2 -//│ JS (unsanitized): -//│ let lambda; lambda = function lambda(arg1, arg2) { return arg1 + arg2 }; new lambda(2, 2) -//│ = [object Object] - :re new() //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor @@ -43,7 +34,7 @@ new { x = 1 } //│ rhs = IntLit of 1 //│ rft = N //│ ╔══[ERROR] Name not found: x -//│ ║ l.37: new { x = 1 } +//│ ║ l.28: new { x = 1 } //│ ╙── ^ From fc8e400eb1fa9886b78a3cfa0769c767797d169f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 23:04:51 +0800 Subject: [PATCH 081/127] restore the test --- hkmc2/shared/src/test/mlscript/codegen/BadNew.mls | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 5176135c5..1638500ff 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -15,6 +15,12 @@ new 2 new() //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor +:sjs +new 2 + 2 +//│ JS (unsanitized): +//│ let lambda; lambda = function lambda(arg1, arg2) { return arg1 + arg2 }; new lambda(2, 2) +//│ = [object Object] + :re new {} //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor From 632ca98a265a80916e40917878d2d7953d0850f3 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 23:05:25 +0800 Subject: [PATCH 082/127] move the test to the correct place --- hkmc2/shared/src/test/mlscript/codegen/BadNew.mls | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 1638500ff..72d5d5e3b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -11,16 +11,16 @@ new new 2 //│ ═══[RUNTIME ERROR] TypeError: 2 is not a constructor -:re -new() -//│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor - :sjs new 2 + 2 //│ JS (unsanitized): //│ let lambda; lambda = function lambda(arg1, arg2) { return arg1 + arg2 }; new lambda(2, 2) //│ = [object Object] +:re +new() +//│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor + :re new {} //│ ═══[RUNTIME ERROR] TypeError: runtime.Unit is not a constructor From e3e989e0904f17aab3225d8e618130307e62428b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 23:06:16 +0800 Subject: [PATCH 083/127] Fix line numbers --- hkmc2/shared/src/test/mlscript/codegen/BadNew.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 72d5d5e3b..e151bc319 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -40,7 +40,7 @@ new { x = 1 } //│ rhs = IntLit of 1 //│ rft = N //│ ╔══[ERROR] Name not found: x -//│ ║ l.28: new { x = 1 } +//│ ║ l.34: new { x = 1 } //│ ╙── ^ From b93112f8d6d5fcc85abb50a22cfe54ac04a236db Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Wed, 12 Feb 2025 23:15:59 +0800 Subject: [PATCH 084/127] update compile tests --- .../src/test/mlscript-compile/Predef.mjs | 24 +++++--- .../src/test/mlscript-compile/Stack.mjs | 6 +- .../test/mlscript-compile/apps/Accounting.mjs | 61 +++++++++++-------- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index e8f9b6375..2a05b2800 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -264,15 +264,16 @@ Predef1 = class Predef { } } static render(arg1) { - let ts, p, scrut, scrut1, scrut2, nme, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; + let ts, p, scrut, scrut1, scrut2, nme, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, lambda, lambda1, lambda2; if (arg1 === undefined) { return "undefined" } else if (arg1 === null) { return "null" } else if (arg1 instanceof globalThis.Array) { - tmp = Predef.fold((arg11, arg2) => { + lambda = function lambda(arg11, arg2) { return arg11 + arg2 - }); + }; + tmp = Predef.fold(lambda); tmp1 = Predef.interleave(", "); tmp2 = Predef.map(Predef.render); tmp3 = runtime.safeCall(tmp2(...arg1)); @@ -281,18 +282,20 @@ Predef1 = class Predef { } else if (typeof arg1 === 'string') { return runtime.safeCall(globalThis.JSON.stringify(arg1)) } else if (arg1 instanceof globalThis.Set) { - tmp5 = Predef.fold((arg11, arg2) => { + lambda1 = function lambda(arg11, arg2) { return arg11 + arg2 - }); + }; + tmp5 = Predef.fold(lambda1); tmp6 = Predef.interleave(", "); tmp7 = Predef.map(Predef.render); tmp8 = runtime.safeCall(tmp7(...arg1)); tmp9 = runtime.safeCall(tmp6(...tmp8)); return runtime.safeCall(tmp5("Set{", ...tmp9, "}")) } else if (arg1 instanceof globalThis.Map) { - tmp10 = Predef.fold((arg11, arg2) => { + lambda2 = function lambda(arg11, arg2) { return arg11 + arg2 - }); + }; + tmp10 = Predef.fold(lambda2); tmp11 = Predef.interleave(", "); tmp12 = Predef.map(Predef.render); tmp13 = runtime.safeCall(tmp12(...arg1)); @@ -435,7 +438,7 @@ Predef1 = class Predef { throw globalThis.Error("unreachable"); } static checkArgs(functionName, expected, isUB, got) { - let scrut, name, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; + let scrut, name, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, lambda; tmp = got < expected; tmp1 = got > expected; tmp2 = isUB && tmp1; @@ -449,9 +452,10 @@ Predef1 = class Predef { tmp4 = ""; } name = tmp4; - tmp5 = Predef.fold((arg11, arg2) => { + lambda = function lambda(arg11, arg2) { return arg11 + arg2 - }); + }; + tmp5 = Predef.fold(lambda); if (isUB === true) { tmp6 = ""; } else { diff --git a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs index a51c90c6a..b52b8c217 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs @@ -91,7 +91,8 @@ Stack1 = class Stack { static zip(...xss) { let go, tmp, tmp1; go = function go(heads, tails) { - return (caseScrut) => { + let lambda; + lambda = function lambda(caseScrut) { let param0, param1, h, t, param01, param11, h2, t2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; if (caseScrut instanceof Stack.Cons.class) { param0 = caseScrut.head; @@ -132,7 +133,8 @@ Stack1 = class Stack { } else { throw new globalThis.Error("match error"); } - } + }; + return lambda }; tmp = go(Stack.Nil, Stack.Nil); tmp1 = Stack.fromArray(xss); diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs index 9dbffae19..a882d2656 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs @@ -68,48 +68,53 @@ Accounting1 = class Accounting { return fs.appendFileSync(this.fileName, tmp) } init() { - let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; + let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, lambda, lambda1; tmp = this.wln(""); tmp1 = Str.concat2("|", "Year"); tmp2 = Str.concat2(tmp1, "|"); - tmp3 = runtime.safeCall(this$Accounting.lines.map((x) => { + lambda = function lambda(x) { return x.name - })); + }; + tmp3 = runtime.safeCall(this$Accounting.lines.map(lambda)); tmp4 = runtime.safeCall(tmp3.join("|")); tmp5 = Str.concat2(tmp2, tmp4); tmp6 = Str.concat2(tmp5, "|"); tmp7 = this.wln(tmp6); tmp8 = Str.concat2("|", "---"); tmp9 = Str.concat2(tmp8, "|"); - tmp10 = runtime.safeCall(this$Accounting.lines.map((x) => { + lambda1 = function lambda(x) { return "--:" - })); + }; + tmp10 = runtime.safeCall(this$Accounting.lines.map(lambda1)); tmp11 = runtime.safeCall(tmp10.join("|")); tmp12 = Str.concat2(tmp9, tmp11); tmp13 = Str.concat2(tmp12, "|"); return this.wln(tmp13) } snapShot(label) { - let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, lambda; tmp = runtime.safeCall(globalThis.String(label)); tmp1 = Str.concat2("|", tmp); tmp2 = Str.concat2(tmp1, "|"); - tmp3 = runtime.safeCall(this$Accounting.lines.map((x) => { + lambda = function lambda(x) { return this$Accounting.display(x.balance) - })); + }; + tmp3 = runtime.safeCall(this$Accounting.lines.map(lambda)); tmp4 = runtime.safeCall(tmp3.join("|")); tmp5 = Str.concat2(tmp2, tmp4); tmp6 = Str.concat2(tmp5, "|"); return this.wln(tmp6) } wrapUp() { - let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; + let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, lambda, lambda1, lambda2, lambda3, lambda4, lambda5, lambda6; tmp = this.wln(""); - tmp1 = runtime.safeCall(this$Accounting.warnings.forEach((x) => { + const this$Report = this; + lambda = function lambda(x) { let tmp27; - tmp27 = this.wln(x); - return this.wln("") - })); + tmp27 = this$Report.wln(x); + return this$Report.wln("") + }; + tmp1 = runtime.safeCall(this$Accounting.warnings.forEach(lambda)); tmp2 = this.wln("### Remaining Available Funds"); tmp3 = this.wln(""); tmp4 = Str.concat2("|", "Summary"); @@ -120,30 +125,36 @@ Accounting1 = class Accounting { tmp9 = this.wln(tmp8); tmp10 = Str.concat2("|", "Matchable"); tmp11 = Str.concat2(tmp10, "|"); - tmp12 = runtime.safeCall(this$Accounting.lines.filter((x) => { + lambda1 = function lambda(x) { return x.isMatchable - })); - tmp13 = runtime.safeCall(tmp12.map((x) => { + }; + tmp12 = runtime.safeCall(this$Accounting.lines.filter(lambda1)); + lambda2 = function lambda(x) { return x.balance - })); - tmp14 = tmp13.reduce((a, b) => { + }; + tmp13 = runtime.safeCall(tmp12.map(lambda2)); + lambda3 = function lambda(a, b) { return a + b - }, 0); + }; + tmp14 = tmp13.reduce(lambda3, 0); tmp15 = this$Accounting.display(tmp14); tmp16 = Str.concat2(tmp11, tmp15); tmp17 = Str.concat2(tmp16, "|"); tmp18 = this.wln(tmp17); tmp19 = Str.concat2("|", "Non-matchable"); tmp20 = Str.concat2(tmp19, "|"); - tmp21 = runtime.safeCall(this$Accounting.lines.filter((x) => { + lambda4 = function lambda(x) { return Predef.not(x.isMatchable) - })); - tmp22 = runtime.safeCall(tmp21.map((x) => { + }; + tmp21 = runtime.safeCall(this$Accounting.lines.filter(lambda4)); + lambda5 = function lambda(x) { return x.balance - })); - tmp23 = tmp22.reduce((a, b) => { + }; + tmp22 = runtime.safeCall(tmp21.map(lambda5)); + lambda6 = function lambda(a, b) { return a + b - }, 0); + }; + tmp23 = tmp22.reduce(lambda6, 0); tmp24 = this$Accounting.display(tmp23); tmp25 = Str.concat2(tmp20, tmp24); tmp26 = Str.concat2(tmp25, "|"); From 3ee9e4c941b76449cb0d55cac029c6e85d467725 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 13 Feb 2025 12:49:24 +0800 Subject: [PATCH 085/127] address pr comments --- hkmc2/shared/src/main/scala/hkmc2/Config.scala | 2 +- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index f9ef7b258..22b5f59e3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -11,7 +11,7 @@ def config(using Config): Config = summon case class Config( sanityChecks: Opt[SanityChecks], effectHandlers: Opt[EffectHandlers], - liftDefns: Opt[LiftDefns] + liftDefns: Opt[LiftDefns], ): def stackSafety: Opt[StackSafety] = effectHandlers.flatMap(_.stackSafety) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index c6ea03bf7..83dd2067f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -353,7 +353,7 @@ sealed abstract class Result: case Value.Lam(params, body) => body :: Nil case Value.Arr(elems) => elems.flatMap(_.value.subBlocks) case _ => Nil - + lazy val freeVars: Set[Local] = this match case Call(fun, args) => fun.freeVars ++ args.flatMap(_.value.freeVars).toSet case Instantiate(cls, args) => cls.freeVars ++ args.flatMap(_.freeVars).toSet From 5099394f464d93a94d61033ca790cd9dbe425238 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Thu, 13 Feb 2025 13:36:01 +0800 Subject: [PATCH 086/127] Update --- .../src/main/scala/hkmc2/codegen/StackSafeTransform.scala | 2 +- hkmc2/shared/src/test/mlscript/codegen/While.mls | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index 33132363e..3530cd233 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -147,7 +147,7 @@ class StackSafeTransform(depthLimit: Int)(using State): val ClsLikeDefn(owner, isym, sym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor) = defn ClsLikeDefn( - owner, isym, sym, k, paramsOpt, parentPath, methods.map(rewriteFn), privateFields, + owner, isym, sym, k, paramsOpt, auxParams, parentPath, methods.map(rewriteFn), privateFields, publicFields, rewriteBlk(preCtor), if isTopLevel && (defn.k is syntax.Mod) then transformTopLevel(ctor) else rewriteBlk(ctor) ) diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index 4a0ee640d..15b8cd57f 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -259,7 +259,7 @@ while print("Hello World"); false then 0(0) else 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.255: then 0(0) +//│ ║ l.259: then 0(0) //│ ╙── ^^^^ //│ ═══[ERROR] Unrecognized term split (false literal). //│ > Hello World From b9e30e66cde0de6f7558ce2039c6b1741d43bcc6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 14 Feb 2025 00:15:36 +0800 Subject: [PATCH 087/127] Don't lift classes directly in a module, fix defns not being lifted out of ignored classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 86 +++++++++----- .../src/test/mlscript/lifter/ClassInFun.mls | 5 +- .../test/mlscript/lifter/ModulesObjects.mls | 108 +++++++++++++++--- 3 files changed, 152 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index c78e0428b..90671afa3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -139,11 +139,13 @@ class Lifter(using State, Raise): * @param localCaptureSyms The symbols in a capture corresponding to a particular local * @param prevFnLocals Locals belonging to function definitions that have already been traversed * @param prevClsDefns Class definitions that have already been traversed, excluding modules + * @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 replDefns Ignored definitions whose definitions we should replace in the block itself */ case class LifterCtx( val defns: Map[BlockMemberSymbol, Defn] = Map.empty, @@ -156,11 +158,13 @@ class Lifter(using State, Raise): val localCaptureSyms: Map[Local, LocalSymbol & NamedSymbol] = Map.empty, val prevFnLocals: FreeVars = FreeVars.empty, val prevClsDefns: List[ClsLikeDefn] = Nil, + val curModules: List[ClsLikeDefn] = Nil, val capturePaths: Map[BlockMemberSymbol, Path] = Map.empty, val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo] = Map.empty, // required captures val ignoredBmsPaths: Map[BlockMemberSymbol, Local] = Map.empty, val localPaths: Map[Local, Local] = Map.empty, val isymPaths: Map[InnerSymbol, Local] = Map.empty, + val replDefns: Map[BlockMemberSymbol, Defn] = Map.empty, ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -196,6 +200,11 @@ class Lifter(using State, Raise): def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) def addIsymPaths(mp: Map[InnerSymbol, Local]) = copy(isymPaths = isymPaths ++ mp) + def addReplDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(replDefns = replDefns ++ mp) + def inModule(defn: ClsLikeDefn) = copy(curModules = defn :: curModules) + def flushModules = + // called when we are lifted out while in some modules, so we need to add the modules' isym paths + copy(curModules = Nil).addIsymPaths(curModules.map(d => d.isym -> d.sym).toMap) object LifterCtx: def empty = LifterCtx() @@ -308,9 +317,9 @@ class Lifter(using State, Raise): ) // d is a top-level definition - // returns (unliftable classes, modules, objects) + // returns (ignored classes, modules, objects) def createMetadata(d: Defn, ctx: LifterCtx): (Set[BlockMemberSymbol], List[ClsLikeDefn], List[ClsLikeDefn]) = - var unliftable: Set[BlockMemberSymbol] = Set.empty + var ignored: Set[BlockMemberSymbol] = Set.empty var clsSymToBms: Map[Local, BlockMemberSymbol] = Map.empty var modules: List[ClsLikeDefn] = Nil var objects: List[ClsLikeDefn] = Nil @@ -340,7 +349,7 @@ class Lifter(using State, Raise): super.applyDefn(defn) walker.applyDefn(d) - // search for defns nested within a top-level module, which can always be lifted + // 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 => @@ -350,7 +359,8 @@ class Lifter(using State, Raise): case c: ClsLikeDefn if c.k is syntax.Mod => true case _ => false - val liftable = if isMod then inModuleDefns(d) else Set.empty + val inModTopLevel = if isMod then inModuleDefns(d) else Set.empty + ignored ++= inModTopLevel val clsSyms = clsSymToBms.values.toSet val walker2 = new BlockTransformer(SymbolSubst()): @@ -358,12 +368,12 @@ class Lifter(using State, Raise): cse match case Case.Cls(cls, path) => clsSymToBms.get(cls) match - case Some(value) if !liftable.contains(value) => + case Some(value) if !ignored.contains(value) => // don't generate a warning if it's already ignored raise(WarningReport( msg"Cannot yet lift the class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation )) - unliftable += value + ignored += value case _ => () case _ => () cse @@ -415,12 +425,12 @@ class Lifter(using State, Raise): msg"Cannot yet lift the class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation )) - unliftable += l + ignored += l v case _ => super.applyValue(v) walker2.applyDefn(d) - (unliftable, modules, objects) + (ignored, modules, objects) extension (b: Block) private def floatOut(ctx: LifterCtx) = @@ -522,7 +532,10 @@ class Lifter(using State, Raise): blockBuilder .assign(sym, Call(d.sym.asPath, Nil)(true, false)) .rest(applyBlock(rest)) - case _ => super.applyBlock(b) + case _ => ctx.replDefns.get(d.sym) match + case Some(value) => Define(value, applyBlock(rest)) + case None => super.applyBlock(b) + case _ => super.applyBlock(b) @@ -621,7 +634,7 @@ class Lifter(using State, Raise): end rewriteBms - // rewrites references to variables + // rewrites references to variables and defns val transformer1 = refRewriter(ctx, ctorCls) // rewrites references to block member symbols @@ -710,7 +723,7 @@ class Lifter(using State, Raise): val newCtx = ctx .replCapturePaths(newCapturePaths) .replLocalPaths(newLocalsPaths) - .replIsymPaths(newIsymPaths) + .addIsymPaths(newIsymPaths) .replIgnoredBmsPaths(newBmsPaths) d match @@ -816,11 +829,13 @@ class Lifter(using State, Raise): case _ => Lifted(d, Nil) def liftDefnsInCls(c: ClsLikeDefn, ctx: LifterCtx): Lifted[ClsLikeDefn] = - val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctx) - val (ctor, ctorDefns) = c.ctor.floatOut(ctx) + val ctxx = if c.k is syntax.Mod then ctx.inModule(c) else ctx + + val (preCtor, preCtorDefns) = c.preCtor.floatOut(ctxx) + val (ctor, ctorDefns) = c.ctor.floatOut(ctxx) val allCtorDefns = preCtorDefns ++ ctorDefns - val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctx.ignored(d.sym)) + val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctxx.ignored(d.sym)) // if this is a module, add a method to reference the classes lifted out val extraMethods = if c.k is syntax.Mod then ctorIncluded.collect: @@ -833,7 +848,7 @@ class Lifter(using State, Raise): ) else Nil val nestedClsPaths: Map[Local, Local] = ctorIncluded.map: - case c: ClsLikeDefn if modOrObj(c) => ctx.modLocals.get(c.sym) match + case c: ClsLikeDefn if modOrObj(c) => ctxx.modLocals.get(c.sym) match case Some(sym) => S(c.sym -> sym) case _ => S(c.sym -> c.sym) case _ => None @@ -841,38 +856,49 @@ class Lifter(using State, Raise): case Some(x) => x .toMap - val newCtx_ = ctx + val newCtx_ = ctxx .addLocalPaths(nestedClsPaths) .addLocalPaths(getVars(c).map(s => s -> s).toMap) val newCtx = c.k match case syntax.Mod => newCtx_ case _ => newCtx_.addIsymPath(c.isym, c.isym) - - val newPreCtor = rewriteBlk(preCtor, S(c), newCtx) - val newCtor = rewriteBlk(ctor, S(c), newCtx) val ctorDefnsLifted = ctorIncluded.flatMap: defn => - val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx) + val Lifted(liftedDefn, extraDefns) = liftOutDefnCont(c, defn, newCtx.flushModules) liftedDefn :: extraDefns + val ctorIgnoredLift = ctorIgnored.map: defn => + liftOutDefnCont(c, defn, newCtx) + + val ctorIgnoredExtra = ctorIgnoredLift.flatMap(_.extraDefns) + val ctorIgnoredRewrite = ctorIgnoredLift.map: lifted => + lifted.liftedDefn.sym -> lifted.liftedDefn + .toMap + + val replDefnsCtx = newCtx.addReplDefns(ctorIgnoredRewrite) + val newPreCtor = rewriteBlk(preCtor, S(c), replDefnsCtx) + val newCtor = rewriteBlk(ctor, S(c), replDefnsCtx) + + 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 - val extras = (ctorDefnsLifted ++ fExtra).map: + val extras = (ctorDefnsLifted ++ fExtra ++ ctorIgnoredExtra).map: case f: FunDefn => f.copy(owner = N) case c: ClsLikeDefn => c.copy(owner = N) case d => d def rewriteExtends(p: Path): Path = p match - case RefOfBms(b) => + case RefOfBms(b) if !ctx.ignored(b) => // we may need to add `class` in case the lifting added extra params - if ctx.getBmsReqdInfo(b).isDefined then Select(b.asPath, Tree.Ident("class"))(N) + if ctxx.getBmsReqdInfo(b).isDefined then Select(b.asPath, Tree.Ident("class"))(N) else b.asPath - case Select(RefOfBms(b), Tree.Ident("class")) => Select(b.asPath, Tree.Ident("class"))(N) + case Select(RefOfBms(b), Tree.Ident("class")) if !ctx.ignored(b) => + Select(b.asPath, Tree.Ident("class"))(N) case _ => return p // if this class extends something, rewrite @@ -915,12 +941,17 @@ class Lifter(using State, Raise): val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) // lift out the nested defns - val nestedLifted = included.map(liftOutDefnCont(f, _, nestedCtx)) - val ignoredExtra = ignored.flatMap(liftOutDefnCont(f, _, nestedCtx).extraDefns) + val nestedLifted = included.map(liftOutDefnCont(f, _, nestedCtx.flushModules)) + 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 = rewriteBlk(blk, N, captureCtx) + val transformed = rewriteBlk(blk, N, captureCtx.addReplDefns(ignoredRewrite)) if thisVars.reqCapture.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) @@ -1005,7 +1036,6 @@ class Lifter(using State, Raise): val ctxx = ctx .addIgnored(unliftable) .withModLocals(modLocals) - .addIsymPaths(modules.map(m => m.isym -> m.sym).toMap) val walker1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 61af2958a..69a1f2436 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -501,10 +501,7 @@ f(2).get() //│ Test11.class = class Test10 { //│ constructor() {} //│ get() { -//│ h4 = function h() { -//│ return x -//│ }; -//│ return h4() +//│ return h$4(this, x) //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index bd0e84317..509785c1d 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -215,31 +215,28 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ JS (unsanitized): -//│ let A9, M21, tmp4; -//│ A9 = function A() { -//│ return new A.class(); -//│ }; -//│ A9.class = class A8 { -//│ constructor() {} -//│ get get() { -//│ return M21.x + M21.x; -//│ } -//│ toString() { return "A(" + "" + ")"; } -//│ }; +//│ let M21, tmp4; //│ M21 = class M20 { //│ static { //│ let scrut, tmp5; +//│ this.A = function A() { +//│ return new A.class(); +//│ }; +//│ this.A.class = class A7 { +//│ constructor() {} +//│ get get() { +//│ return M21.x + M20.x; +//│ } +//│ toString() { return "A(" + "" + ")"; } +//│ }; //│ scrut = M20.A(); -//│ if (scrut instanceof A9.class) { +//│ if (scrut instanceof M20.A.class) { //│ tmp5 = 2; //│ } else { //│ tmp5 = 3; //│ } //│ this.x = tmp5; //│ } -//│ static get A() { -//│ return A9; -//│ } //│ static toString() { return "M"; } //│ }; //│ tmp4 = M21.A(); @@ -253,3 +250,84 @@ module M with class B() extends A M.B().get //│ = 2 + +/// Top Level Modules /// + +// newA should reference the class symbol +// newA_B should reference the BlockMemberSymbol +:lift +:sjs +module M with + class A(x) with + class B() with + fun get = x + fun newA_B(y) = A(y) + fun newA = A() + fun newB = B() + 0 is A +M.A(2).newB.newA_B(3).newB.get +//│ JS (unsanitized): +//│ let B2, M25, tmp6, tmp7, B$ctor, B$; +//│ B$ = function B$(A$instance$0) { +//│ let tmp8; +//│ tmp8 = new B2.class(); +//│ return tmp8(A$instance$0) +//│ }; +//│ B$ctor = function B$ctor(A$instance$0) { +//│ return () => { +//│ let tmp8; +//│ tmp8 = new B2.class(); +//│ return tmp8(A$instance$0) +//│ } +//│ }; +//│ B2 = function B() { +//│ return (A$instance$01) => { +//│ return new B.class()(A$instance$01); +//│ } +//│ }; +//│ B2.class = class B1 { +//│ constructor() { +//│ return (A$instance$0) => { +//│ this.A$instance$0 = A$instance$0; +//│ return this; +//│ } +//│ } +//│ get get() { +//│ return this.A$instance$0.x; +//│ } +//│ newA_B(y) { +//│ return M25.A(y) +//│ } +//│ toString() { return "B(" + "" + ")"; } +//│ }; +//│ M25 = class M24 { +//│ static { +//│ let scrut; +//│ this.A = function A(x1) { +//│ return new A.class(x1); +//│ }; +//│ this.A.class = class A9 { +//│ constructor(x) { +//│ this.x = x; +//│ } +//│ get newA() { +//│ return M24.A(); +//│ } +//│ get newB() { +//│ return B$(this); +//│ } +//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } +//│ }; +//│ scrut = 0; +//│ if (scrut instanceof M24.A.class) { +//│ true +//│ } else { +//│ false +//│ } +//│ } +//│ static toString() { return "M"; } +//│ }; +//│ tmp6 = M25.A(2); +//│ tmp7 = runtime.safeCall(tmp6.newB.newA_B(3)); +//│ tmp7.newB.get +//│ = 3 From ca2cdfc4145dd1bb4adc3514483eb4510ba9ed4c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 14 Feb 2025 00:31:25 +0800 Subject: [PATCH 088/127] don't rewrite ignored objects --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 4 ++-- hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 90671afa3..98ebed397 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -523,7 +523,7 @@ class Lifter(using State, Raise): case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) case Define(d: Defn, rest: Block) => ctx.modLocals.get(d.sym) match - case Some(sym) => ctx.getBmsReqdInfo(d.sym) match + case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match case Some(_) => blockBuilder .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) @@ -839,7 +839,7 @@ class Lifter(using State, Raise): // if this is a module, add a method to reference the classes lifted out val extraMethods = if c.k is syntax.Mod then ctorIncluded.collect: - case cls: ClsLikeDefn if cls.k is syntax.Cls => + case cls: ClsLikeDefn if (cls.k is syntax.Cls) && !ctx.ignored(cls.sym) => FunDefn( S(c.isym), BlockMemberSymbol(cls.sym.nme, Nil), diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 509785c1d..c7996df8c 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -331,3 +331,7 @@ M.A(2).newB.newA_B(3).newB.get //│ tmp7 = runtime.safeCall(tmp6.newB.newA_B(3)); //│ tmp7.newB.get //│ = 3 + +module M with + class A + object B extends A From d47ce44aaf2835f7af2adc6f1b9b1bb2781c53f6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 14 Feb 2025 00:34:06 +0800 Subject: [PATCH 089/127] remove unneeded case --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 98ebed397..8567a3095 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -580,14 +580,6 @@ class Lifter(using State, Raise): case None => super.applyPath(p) case _ => super.applyPath(p) - override def applyDefn(defn: Defn): Defn = defn match - case c: ClsLikeDefn => - val par2 = c.parentPath.mapConserve(applyPath) - if par2 is c.parentPath then defn - else c.copy(parentPath = par2) - case _ => super.applyDefn(defn) - - def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, From 19cab271556896b250b78c86855476c89c64c94f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 14 Feb 2025 01:07:00 +0800 Subject: [PATCH 090/127] fix imports --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 51 ++++++++++++------- .../src/test/mlscript/lifter/Imports.mls | 22 ++++++++ 2 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/lifter/Imports.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 8567a3095..16b274c8e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -129,8 +129,10 @@ class Lifter(using State, Raise): /** * The context of the class lifter. One can create an empty context using `Lifter.empty`. * - * @param usedLocals Describes the locals belonging to each function that are accessed/mutated by nested definitions. + * @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. @@ -149,6 +151,7 @@ class Lifter(using State, Raise): */ case class LifterCtx( 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, @@ -178,10 +181,12 @@ class Lifter(using State, Raise): def ignored(b: BlockMemberSymbol) = ignoredDefns.contains(b) def isModOrObj(b: BlockMemberSymbol) = modLocals.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 withModLocals(mp: Map[BlockMemberSymbol, Local]) = copy(modLocals = modLocals ++ 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) @@ -552,7 +557,7 @@ class Lifter(using State, Raise): case _ => super.applyPath(p) // Rewrites this.className.class to reference the top-level definition - case s @ Select(RefOfBms(l), Tree.Ident("class")) if !ctx.ignored(l) => + case s @ Select(RefOfBms(l), Tree.Ident("class")) if !ctx.ignored(l) && ctx.isRelevant(l) => // this class will be lifted, rewrite the ref to strip it of `Select` Select(Value.Ref(l), Tree.Ident("class"))(s.symbol) @@ -566,7 +571,7 @@ class Lifter(using State, Raise): // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol // reference is passed as function parameters). - case RefOfBms(l) if ctx.ignored(l) => ctx.getIgnoredBmsPath(l) match + case RefOfBms(l) if ctx.ignored(l) && ctx.isRelevant(l) => ctx.getIgnoredBmsPath(l) match case Some(value) => Value.Ref(value) case None => super.applyPath(p) @@ -885,11 +890,11 @@ class Lifter(using State, Raise): case d => d def rewriteExtends(p: Path): Path = p match - case RefOfBms(b) if !ctx.ignored(b) => + case RefOfBms(b) if !ctx.ignored(b) && ctx.isRelevant(b) => // we may need to add `class` in case the lifting added extra params if ctxx.getBmsReqdInfo(b).isDefined then Select(b.asPath, Tree.Ident("class"))(N) else b.asPath - case Select(RefOfBms(b), Tree.Ident("class")) if !ctx.ignored(b) => + case Select(RefOfBms(b), Tree.Ident("class")) if !ctx.ignored(b) && ctx.isRelevant(b) => Select(b.asPath, Tree.Ident("class"))(N) case _ => return p @@ -1032,15 +1037,17 @@ class Lifter(using State, Raise): val walker1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => - val Lifted(lifted, extra) = d match - case f: FunDefn => liftDefnsInFn(f, ctxx.addBmsReqdInfo(createLiftInfoFn(f, ctxx))) - case c: ClsLikeDefn => liftDefnsInCls(c, ctxx.addBmsReqdInfo(createLiftInfoCls(c, ctxx))) + 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) (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker1.applyBlock(blk) - /** * 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 @@ -1059,7 +1066,8 @@ class UsedVarAnalyzer(b: Block)(using State): 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 directly nested within another defn (shallow) + 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 ) private def createMetadata: DefnMetadata = @@ -1068,9 +1076,12 @@ class UsedVarAnalyzer(b: Block)(using State): 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 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 @@ -1082,6 +1093,7 @@ class UsedVarAnalyzer(b: Block)(using State): 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) @@ -1089,12 +1101,9 @@ class UsedVarAnalyzer(b: Block)(using State): for d <- thisScopeDefns do nestedIn += (d.sym -> f.sym) createMetadataDefn(d, newExisting, newInScope) + nested ++= nestedDeep(d.sym) - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = - createMetadataDefn(defn, newExisting, inScope) - defn - walker.applyBlock(f.body) + nestedDeep += f.sym -> nested def createMetadataDefn(d: Defn, existing: Set[Local], inScope: Set[BlockMemberSymbol]): Unit = d match @@ -1105,6 +1114,8 @@ class UsedVarAnalyzer(b: Block)(using State): 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 @@ -1117,6 +1128,7 @@ class UsedVarAnalyzer(b: Block)(using State): 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) @@ -1124,6 +1136,9 @@ class UsedVarAnalyzer(b: Block)(using State): for d <- thisScopeDefns do nestedIn += (d.sym -> c.sym) createMetadataDefn(d, newExisting, newInScope) + nested ++= nestedDeep(d.sym) + + nestedDeep += c.sym -> nested val walker = new BlockTransformerShallow(SymbolSubst()): override def applyDefn(defn: Defn): Defn = @@ -1131,10 +1146,10 @@ class UsedVarAnalyzer(b: Block)(using State): createMetadataDefn(defn, b.definedVars, Set.empty) defn walker.applyBlock(b) - DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedIn) + DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn) - val DefnMetadata(definedLocals, defnsMap, - existingVars, inScopeDefns, nestedDefns, nestedIn) = createMetadata + val DefnMetadata(definedLocals, defnsMap, existingVars, + inScopeDefns, nestedDefns, nestedDeep, nestedIn) = createMetadata def isModule(s: BlockMemberSymbol) = defnsMap.get(s) match case S(c: ClsLikeDefn) => c.k is syntax.Mod diff --git a/hkmc2/shared/src/test/mlscript/lifter/Imports.mls b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls new file mode 100644 index 000000000..0598758b9 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/lifter/Imports.mls @@ -0,0 +1,22 @@ +:js +:lift + +import "../../mlscript-compile/Option.mls" + +:sjs +module A with + fun f(x) = x is Option.Some +//│ JS (unsanitized): +//│ let A1; +//│ A1 = class A { +//│ static {} +//│ static f(x) { +//│ if (x instanceof Option.Some.class) { +//│ return true +//│ } else { +//│ return false +//│ } +//│ } +//│ static toString() { return "A"; } +//│ }; + From ab0902e105c686e95303cf718695312c9321da82 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Fri, 21 Feb 2025 21:47:31 +0800 Subject: [PATCH 091/127] fix freevars --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index fd61ea669..21b210bf3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -92,7 +92,7 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, finallyDo, rest) => sub.freeVars ++ finallyDo.freeVars ++ rest.freeVars case Assign(lhs, rhs, rest) => Set(lhs) ++ rhs.freeVars ++ rest.freeVars case AssignField(lhs, nme, rhs, rest) => lhs.freeVars ++ rhs.freeVars ++ rest.freeVars - case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVarsLLIR ++ fld.freeVarsLLIR ++ rhs.freeVarsLLIR ++ rest.freeVarsLLIR + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => lhs.freeVars ++ fld.freeVars ++ rhs.freeVars ++ rest.freeVars case Define(defn, rest) => defn.freeVars ++ rest.freeVars case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => (bod.freeVars - lhs) ++ rst.freeVars ++ hdr.flatMap(_.freeVars) From 133f0f4cf423b372c5a5c0021c530279196c23f8 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Sat, 22 Feb 2025 15:43:38 +0800 Subject: [PATCH 092/127] Add a couple of test cases --- .../src/test/mlscript/backlog/Lifter.mls | 17 ++++--- .../src/test/mlscript/backlog/ToTriage.mls | 44 +++++++++++++++++++ .../test/mlscript/lifter/ModulesObjects.mls | 13 ++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 7a45bc7ea..6b83d3faf 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -60,10 +60,17 @@ fun foo(x, n) = if n > 0 then res + foo(C(), n - 1) else "" //│ ═══[WARNING] Cannot yet lift the class/module `C` as it is used in an instance check. -:todo +:todo // should return the function that creates C instances +:w +fun foo() = + class C() + C +//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. + :w fun foo() = class C() + fun f = C() C //│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. @@ -96,13 +103,13 @@ fun foo(x, y) = M.foo() //│ ═══[WARNING] Modules are not yet properly lifted and will break. //│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M1, foo2;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp2; M.y$1 = 2; tmp2 = M.x$0 + M.y$1; return tmp2 + M.test } static toString() { return "M"; } }; foo2 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res9 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > let M1, foo3;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp2; M.y$1 = 2; tmp2 = M.x$0 + M.y$1; return tmp2 + M.test } static toString() { return "M"; } }; foo3 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res10 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement :expect 14 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: foo2 is not defined +//│ ═══[RUNTIME ERROR] ReferenceError: foo3 is not defined //│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' fun foo(x, y) = @@ -118,7 +125,7 @@ fun foo(x, y) = :expect 12 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: foo4 is not defined +//│ ═══[RUNTIME ERROR] ReferenceError: foo5 is not defined //│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' @@ -128,7 +135,7 @@ class A(x) with fun getA() = M.getB() //│ ═══[WARNING] Modules are not yet properly lifted and will break. //│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M5, A6;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A6 = function A(...args1) { return new A.class(...args1); }; A6.class = class A5 { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res13 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } +//│ > let M5, A6;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A6 = function A(...args1) { return new A.class(...args1); }; A6.class = class A5 { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res14 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } //│ > ^^^^^^ //│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index eb6abf8df..b08af4bbf 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -319,3 +319,47 @@ Int // ——— ——— ——— +class C + +:sjs +class D extends id(C) +//│ JS (unsanitized): +//│ let D1; +//│ D1 = class D extends Predef.id.class { +//│ constructor() { +//│ super(C1); +//│ } +//│ toString() { return "D"; } +//│ }; +//│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null + +// ——— ——— ——— + +let x = 0 +//│ x = 0 + +set x += 1; () +//│ /!!!\ Uncaught error: scala.MatchError: LetLike(keyword 'set',App(Ident(;),Tup(List(App(Ident(+=),Tup(List(Ident(x), IntLit(1)))), Unt()))),None,None) (of class hkmc2.syntax.Tree$LetLike) + +[x, set x += 1; x] +//│ /!!!\ Uncaught error: scala.MatchError: LetLike(keyword 'set',App(Ident(;),Tup(List(App(Ident(+=),Tup(List(Ident(x), IntLit(1)))), Ident(x)))),None,None) (of class hkmc2.syntax.Tree$LetLike) + +// ——— ——— ——— + +// TODO `baz()` should be in tail position (this is due to its by-name nature) +:sjs +fun baz = 1 +fun bar() = baz +//│ JS (unsanitized): +//│ let bar, baz; +//│ baz = function baz() { +//│ return 1 +//│ }; +//│ bar = function bar() { +//│ let tmp10; +//│ tmp10 = baz(); +//│ return tmp10 +//│ }; + +// ——— ——— ——— + diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index c7996df8c..5578167da 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -335,3 +335,16 @@ M.A(2).newB.newA_B(3).newB.get module M with class A object B extends A + + +:lift +fun foo(x) = + object O with + fun bar = x + val baz = x + [x === O.bar, set x += 1 in [x === O.bar, x === O.baz], x === O.bar] + +foo(123) +//│ = [true, [true, false], true] + + From b17ffec0605580de70e702e77ae9d6c4b04497c8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:03:55 +0800 Subject: [PATCH 093/127] port fixes from benchmark branch --- .../hkmc2/codegen/BlockTransformer.scala | 9 +-- .../src/main/scala/hkmc2/codegen/Lifter.scala | 68 +++++++--------- .../src/test/mlscript/backlog/Lifter.mls | 26 +++--- .../test/mlscript/lifter/ModulesObjects.mls | 81 +++++++++---------- 4 files changed, 81 insertions(+), 103 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 6ac0f2d01..f5ec5e5e1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -117,12 +117,6 @@ class BlockTransformer(subst: SymbolSubst): val sym2 = p.symbol.mapConserve(_.subst) if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2) case v: Value => applyValue(v) - case DynSelect(qual, fld, ai) => - val qual2 = applyPath(qual) - val fld2 = applyPath(fld) - if (qual2 is qual) && (fld2 is fld) - then p - else DynSelect(qual2, fld2, ai) def applyValue(v: Value): Value = v match case Value.Ref(l) => @@ -147,7 +141,8 @@ class BlockTransformer(subst: SymbolSubst): if (own2 is fun.owner) && (sym2 is fun.sym) && (params2 is fun.params) && (body2 is fun.body) then fun else FunDefn(own2, sym2, params2, body2) - def applyDefn(defn: Defn): Defn = defn match + def applyDefn(defn: Defn): Defn = + defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(owner, k, sym, rhs) => val owner2 = owner.mapConserve(_.subst) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 16b274c8e..bd077d5a1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -164,7 +164,7 @@ class Lifter(using State, Raise): val curModules: List[ClsLikeDefn] = Nil, val capturePaths: Map[BlockMemberSymbol, Path] = Map.empty, val bmsReqdInfo: Map[BlockMemberSymbol, LiftedInfo] = Map.empty, // required captures - val ignoredBmsPaths: Map[BlockMemberSymbol, Local] = Map.empty, + val ignoredBmsPaths: Map[BlockMemberSymbol, Path] = Map.empty, val localPaths: Map[Local, Local] = Map.empty, val isymPaths: Map[InnerSymbol, Local] = Map.empty, val replDefns: Map[BlockMemberSymbol, Defn] = Map.empty, @@ -198,11 +198,11 @@ class Lifter(using State, Raise): def addCapturePath(src: BlockMemberSymbol, path: Path) = copy(capturePaths = capturePaths + (src -> path)) def addBmsReqdInfo(mp: Map[BlockMemberSymbol, LiftedInfo]) = copy(bmsReqdInfo = bmsReqdInfo ++ mp) def replLocalPaths(m: Map[Local, Local]) = copy(localPaths = m) - def replIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = m) + def replIgnoredBmsPaths(m: Map[BlockMemberSymbol, Path]) = copy(ignoredBmsPaths = m) def replIsymPaths(m: Map[InnerSymbol, Local]) = copy(isymPaths = m) def addLocalPaths(m: Map[Local, Local]) = copy(localPaths = localPaths ++ m) def addLocalPath(target: Local, path: Local) = copy(localPaths = localPaths + (target -> path)) - def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Local]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) + def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Path]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) def addIsymPaths(mp: Map[InnerSymbol, Local]) = copy(isymPaths = isymPaths ++ mp) def addReplDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(replDefns = replDefns ++ mp) @@ -337,21 +337,24 @@ class Lifter(using State, Raise): // search for modules val walker = new BlockTransformer(SymbolSubst()): override def applyDefn(defn: Defn): Defn = - if defn === d then super.applyDefn(defn) - else defn match - case c: ClsLikeDefn => - clsSymToBms += c.isym -> c.sym - - if c.k is syntax.Mod then - raise(WarningReport( - msg"Modules are not yet properly lifted and will break." -> N :: Nil, - N, Diagnostic.Source.Compilation - )) - modules +:= c - else if c.k is syntax.Obj then - objects +:= c - case _ => () - super.applyDefn(defn) + if defn === d then + super.applyDefn(defn) + else + defn match + case c: ClsLikeDefn => + clsSymToBms += c.isym -> c.sym + + if c.k is syntax.Mod then + 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) walker.applyDefn(d) // search for defns nested within a top-level module, which are unnecessary to lift @@ -458,7 +461,7 @@ class Lifter(using State, Raise): val refBms = inScopeRefs.intersect(ctx.ignoredDefns).toList.sortBy(_.uid) val modLocal = d match - case c: ClsLikeDefn if modOrObj(c) => parentCls match + case c: ClsLikeDefn if modOrObj(c) && !ctx.ignored(c.sym) => parentCls match case None => S(VarSymbol(Tree.Ident(c.sym.nme + "$"))) case Some(value) => S(TermSymbol(syntax.ImmutVal, S(value.isym), Tree.Ident(c.sym.nme + "$"))) case _ => N @@ -496,7 +499,7 @@ class Lifter(using State, Raise): def createLiftInfoCls(c: ClsLikeDefn, ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val defns = c.preCtor.floatOut(ctx)._2 ++ c.ctor.floatOut(ctx)._2 - val newCtx = if c.k is syntax.Mod then ctx else ctx.addClsDefn(c) + val newCtx = if (c.k is syntax.Mod) && !ctx.ignored(c.sym) then ctx else ctx.addClsDefn(c) defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) @@ -572,7 +575,7 @@ class Lifter(using State, Raise): // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol // reference is passed as function parameters). case RefOfBms(l) if ctx.ignored(l) && ctx.isRelevant(l) => ctx.getIgnoredBmsPath(l) match - case Some(value) => Value.Ref(value) + case Some(value) => value case None => super.applyPath(p) // This rewrites naked references to locals. If a function is in a capture, then we select that value @@ -653,14 +656,14 @@ class Lifter(using State, Raise): val localsArgs = info.reqdVars.map(s => ctx.getLocalPath(s).get.asPath.asArg) val capturesArgs = info.reqdCaptures.map(ctx.getCapturePath(_).get.asArg) val iSymArgs = info.reqdInnerSyms.map(ctx.getIsymPath(_).get.asPath.asArg) - val bmsArgs = info.reqdBms.map(ctx.getIgnoredBmsPath(_).get.asPath.asArg) + val bmsArgs = info.reqdBms.map(ctx.getIgnoredBmsPath(_).get.asArg) bmsArgs ++ iSymArgs ++ localsArgs ++ capturesArgs def createCall(sym: BlockMemberSymbol, ctx: LifterCtx): Call = val info = ctx.getBmsReqdInfo(sym).get val callSym = info.fakeCtorBms match case Some(v) => v - case None => sym + case None => sym Call(callSym.asPath, getCallArgs(sym, ctx))(false, false) // deals with creating parameter lists @@ -712,7 +715,7 @@ class Lifter(using State, Raise): val extraParamsBms = bmsSymbols.map: // parameter list case (d, sym) => Param(FldFlags.empty, sym, None) val newBmsPaths = bmsSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym + case (d, sym) => d -> sym.asPath .toMap val extraParams = extraParamsBms ++ extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures @@ -834,16 +837,6 @@ class Lifter(using State, Raise): val allCtorDefns = preCtorDefns ++ ctorDefns val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctxx.ignored(d.sym)) - // if this is a module, add a method to reference the classes lifted out - val extraMethods = if c.k is syntax.Mod then ctorIncluded.collect: - case cls: ClsLikeDefn if (cls.k is syntax.Cls) && !ctx.ignored(cls.sym) => - FunDefn( - S(c.isym), - BlockMemberSymbol(cls.sym.nme, Nil), - Nil, - Return(cls.sym.asPath, false) - ) - else Nil val nestedClsPaths: Map[Local, Local] = ctorIncluded.map: case c: ClsLikeDefn if modOrObj(c) => ctxx.modLocals.get(c.sym) match case Some(sym) => S(c.sym -> sym) @@ -856,9 +849,10 @@ class Lifter(using State, Raise): val newCtx_ = ctxx .addLocalPaths(nestedClsPaths) .addLocalPaths(getVars(c).map(s => s -> s).toMap) + .addIgnoredBmsPaths(ctorIgnored.map(d => d.sym -> Select(c.isym.asPath, Tree.Ident(d.sym.nme))(S(d.sym))).toMap) val newCtx = c.k match - case syntax.Mod => newCtx_ + case syntax.Mod if !ctx.ignored(c.sym) => newCtx_ case _ => newCtx_.addIsymPath(c.isym, c.isym) val ctorDefnsLifted = ctorIncluded.flatMap: defn => @@ -902,7 +896,7 @@ class Lifter(using State, Raise): val newPar = c.parentPath.map(rewriteExtends) val newDef = c.copy( - methods = extraMethods ++ methods, + methods = methods, preCtor = newPreCtor, parentPath = newPar, ctor = newCtor, @@ -934,7 +928,7 @@ class Lifter(using State, Raise): .addCapturePath(f.sym, captureSym.asPath) // the path to this function's capture .addLocalPaths((thisVars.vars.toSet -- thisVars.reqCapture).map(s => s -> s).toMap) .addLocalPaths(modPaths) - .addIgnoredBmsPaths(ignored.map(d => d.sym -> d.sym).toMap) + .addIgnoredBmsPaths(ignored.map(d => d.sym -> d.sym.asPath).toMap) val nestedCtx = captureCtx.addFnLocals(captureCtx.usedLocals(f.sym)) // lift out the nested defns diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 6b83d3faf..229741df8 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -101,16 +101,12 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M1, foo3;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp2; M.y$1 = 2; tmp2 = M.x$0 + M.y$1; return tmp2 + M.test } static toString() { return "M"; } }; foo3 = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res10 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement +//│ ═══[WARNING] Modules are not yet lifted. +//│ ═══[WARNING] Modules are not yet lifted. :expect 14 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: foo3 is not defined -//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' +//│ = 14 fun foo(x, y) = module M with @@ -119,9 +115,9 @@ fun foo(x, y) = x + y fun foo = M.foo() foo -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: M$ (class hkmc2.semantics.VarSymbol) +//│ ═══[WARNING] Modules are not yet lifted. +//│ ═══[WARNING] Modules are not yet lifted. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) :expect 12 foo(10, 0) @@ -133,16 +129,12 @@ class A(x) with module M with fun getB() = x fun getA() = M.getB() -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M5, A6;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A6 = function A(...args1) { return new A.class(...args1); }; A6.class = class A5 { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res14 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement +//│ ═══[WARNING] Modules are not yet lifted. +//│ ═══[WARNING] Modules are not yet lifted. :expect 2 A(2).getA() -//│ ═══[RUNTIME ERROR] ReferenceError: A6 is not defined -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 fun foo(x) = object Foo with diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 5578167da..1a2a8ca3c 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -12,17 +12,12 @@ fun foo(x, y) = set y = 2 x + y + test M.foo() -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M1, foo;try { M1 = class M { static { return (x$0, y$1) => { this.x$0 = x$0; this.y$1 = y$1; this.test = 2; return this; } } static foo(...args) { globalThis.Predef.checkArgs("foo", 0, true, args.length); let tmp; M.y$1 = 2; tmp = M.x$0 + M.y$1; return tmp + M.test } static toString() { return "M"; } }; foo = function foo(...args) { globalThis.Predef.checkArgs("foo", 2, true, args.length); let x = args[0]; let y = args[1]; let M$; M$ = runtime.checkCall(M1(x, y)); return runtime.checkCall(M$.foo()) }; block$res1 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement +//│ ═══[WARNING] Modules are not yet lifted. :todo :expect 14 foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined -//│ ═══[RUNTIME ERROR] Expected: '14', got: 'undefined' +//│ = 14 :todo fun foo(x, y) = @@ -32,9 +27,8 @@ fun foo(x, y) = x + y fun foo = M.foo() foo -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: M$ (class hkmc2.semantics.VarSymbol) +//│ ═══[WARNING] Modules are not yet lifted. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) :fixme :expect 12 @@ -48,17 +42,12 @@ class A(x) with module M with fun getB() = x fun getA() = M.getB() -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ ═══[WARNING] Modules are not yet properly lifted and will break. -//│ > let M5, A1;try { M5 = class M4 { static { return (A$instance$0) => { this.A$instance$0 = A$instance$0; return this; } } static getB(...args) { globalThis.Predef.checkArgs("getB", 0, true, args.length); return M4.A$instance$0.x } static toString() { return "M"; } }; A1 = function A(...args1) { return new A.class(...args1); }; A1.class = class A { constructor(x) { this.x = x; this.M$ = runtime.checkCall(M5(this)); } get getA$__checkNotMethod() { runtime.deboundMethod("getA", "A"); } getA(...args) { globalThis.Predef.checkArgs("getA", 0, true, args.length); return runtime.checkCall(this.M$.getB()) } toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } }; block$res5 = undefined; } catch (e) { console.log('\u200B' + e + '\u200B'); } -//│ > ^^^^^^ -//│ ═══[COMPILATION ERROR] [Uncaught SyntaxError] Illegal return statement +//│ ═══[WARNING] Modules are not yet lifted. :todo :expect 2 A(2).getA() -//│ ═══[RUNTIME ERROR] ReferenceError: A1 is not defined -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ = 2 fun foo(x, y) = @@ -82,19 +71,19 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //│ JS (unsanitized): -//│ let M9, foo31, foo4, foo3$, foo$capture1; -//│ foo3$ = function foo3$(M10, x, foo$capture2) { -//│ return M10.foo2() +//│ let M6, foo31, foo4, foo3$, foo$capture1; +//│ foo3$ = function foo3$(M7, x, foo$capture2) { +//│ return M7.foo2() //│ }; -//│ foo31 = function foo3(M10, x, foo$capture2) { +//│ foo31 = function foo3(M7, x, foo$capture2) { //│ return () => { -//│ return foo3$(M10, x, foo$capture2) +//│ return foo3$(M7, x, foo$capture2) //│ } //│ }; -//│ M9 = function M(x$11, foo$capture$01) { +//│ M6 = function M(x$11, foo$capture$01) { //│ return new M.class()(x$11, foo$capture$01); //│ }; -//│ M9.class = class M8 { +//│ M6.class = class M5 { //│ constructor() { //│ return (x$1, foo$capture$0) => { //│ this.x$1 = x$1; @@ -120,7 +109,7 @@ fun foo(x, y) = //│ foo4 = function foo(x, y) { //│ let tmp1, M$, capture; //│ capture = new foo$capture1(y); -//│ M$ = M9(x, capture); +//│ M$ = M6(x, capture); //│ tmp1 = foo3$(M$, x, capture); //│ return tmp1 //│ }; @@ -136,11 +125,11 @@ class A(x) with fun getB() = x fun getA() = M.getB() //│ JS (unsanitized): -//│ let M11, A3; -//│ M11 = function M(A$instance$01) { +//│ let M8, A3; +//│ M8 = function M(A$instance$01) { //│ return new M.class()(A$instance$01); //│ }; -//│ M11.class = class M10 { +//│ M8.class = class M7 { //│ constructor() { //│ return (A$instance$0) => { //│ this.A$instance$0 = A$instance$0; @@ -158,7 +147,7 @@ class A(x) with //│ A3.class = class A2 { //│ constructor(x) { //│ this.x = x; -//│ this.M$ = M11(this); +//│ this.M$ = M8(this); //│ } //│ getA() { //│ return this.M$.getB() @@ -215,8 +204,8 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ JS (unsanitized): -//│ let M21, tmp4; -//│ M21 = class M20 { +//│ let M18, tmp4; +//│ M18 = class M17 { //│ static { //│ let scrut, tmp5; //│ this.A = function A() { @@ -225,12 +214,12 @@ M.A().get //│ this.A.class = class A7 { //│ constructor() {} //│ get get() { -//│ return M21.x + M20.x; +//│ return M18.x + M17.x; //│ } //│ toString() { return "A(" + "" + ")"; } //│ }; -//│ scrut = M20.A(); -//│ if (scrut instanceof M20.A.class) { +//│ scrut = M17.A(); +//│ if (scrut instanceof M17.A.class) { //│ tmp5 = 2; //│ } else { //│ tmp5 = 3; @@ -239,7 +228,7 @@ M.A().get //│ } //│ static toString() { return "M"; } //│ }; -//│ tmp4 = M21.A(); +//│ tmp4 = M18.A(); //│ tmp4.get //│ = 4 @@ -267,7 +256,7 @@ module M with 0 is A M.A(2).newB.newA_B(3).newB.get //│ JS (unsanitized): -//│ let B2, M25, tmp6, tmp7, B$ctor, B$; +//│ let B2, M22, tmp6, tmp7, B$ctor, B$; //│ B$ = function B$(A$instance$0) { //│ let tmp8; //│ tmp8 = new B2.class(); @@ -296,11 +285,11 @@ M.A(2).newB.newA_B(3).newB.get //│ return this.A$instance$0.x; //│ } //│ newA_B(y) { -//│ return M25.A(y) +//│ return M22.A(y) //│ } //│ toString() { return "B(" + "" + ")"; } //│ }; -//│ M25 = class M24 { +//│ M22 = class M21 { //│ static { //│ let scrut; //│ this.A = function A(x1) { @@ -311,7 +300,7 @@ M.A(2).newB.newA_B(3).newB.get //│ this.x = x; //│ } //│ get newA() { -//│ return M24.A(); +//│ return M21.A(); //│ } //│ get newB() { //│ return B$(this); @@ -319,7 +308,7 @@ M.A(2).newB.newA_B(3).newB.get //│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ scrut = 0; -//│ if (scrut instanceof M24.A.class) { +//│ if (scrut instanceof M21.A.class) { //│ true //│ } else { //│ false @@ -327,7 +316,7 @@ M.A(2).newB.newA_B(3).newB.get //│ } //│ static toString() { return "M"; } //│ }; -//│ tmp6 = M25.A(2); +//│ tmp6 = M22.A(2); //│ tmp7 = runtime.safeCall(tmp6.newB.newA_B(3)); //│ tmp7.newB.get //│ = 3 @@ -347,4 +336,12 @@ fun foo(x) = foo(123) //│ = [true, [true, false], true] - +:fixme +fun f = + module M with + fun g = + fun h = M.g + h + M.g +//│ ═══[WARNING] Modules are not yet lifted. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) From d59bbf9faf754a130c4478931730e2ffb8284b1a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:36:41 +0800 Subject: [PATCH 094/127] Add test and comments --- hkmc2/shared/src/test/mlscript/HkScratch.mls | 210 ++++++++++++++++++ .../src/test/mlscript/backlog/Lifter.mls | 20 +- 2 files changed, 224 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index f46025be7..bea5b397a 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -8,4 +8,214 @@ :d +:sjs +:lift +fun f(x) = + fun g = + if 2 is A do () + class A with + fun h = + fun i = x + i + 2 + 2 +//│ Elab: { ‹› fun member:f‹368›(Param(‹›,x‹369›,None), ) = { ‹› fun member:g‹367› = { if { let $scrut‹370› = 2; scrut is A -> { else () }; else () }; Cls A { ‹› fun member:h‹365› = { ‹› fun member:i‹364› = x‹369›#666; member:i‹364›#666 }; }; 2 }; 2 }; } +//│ FAILURE: Unexpected warning +//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +//│ JS (unsanitized): +//│ let i, g, f, g$, i$; +//│ i$ = function i$(A$instance, x) { +//│ return x +//│ }; +//│ i = function i(A$instance, x) { +//│ return () => { +//│ return i$(A$instance, x) +//│ } +//│ }; +//│ g$ = function g$(x) { +//│ let A1, scrut, tmp; +//│ scrut = 2; +//│ if (scrut instanceof A1) { +//│ tmp = runtime.Unit; +//│ } else { +//│ tmp = runtime.Unit; +//│ } +//│ A1 = class A { +//│ constructor() {} +//│ get h() { +//│ let tmp1; +//│ tmp1 = i$(this, x); +//│ return tmp1; +//│ } +//│ toString() { return "A"; } +//│ }; +//│ return 2 +//│ }; +//│ g = function g(x) { +//│ return () => { +//│ return g$(x) +//│ } +//│ }; +//│ f = function f(x) { +//│ return 2 +//│ }; +//│ FAILURE: Unexpected warning +//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +:lot +:lift +fun foo(x) = + object Foo with + fun self1 = this + fun self2 = Foo + class Bar extends Foo + (new Bar).self2 +foo(2) +//│ Elab: { ‹› fun member:foo‹435›(Param(‹›,x‹436›,None), ) = { Obj Foo { ‹module› fun member:self1‹431› = object:Foo‹437›#0; ‹module› fun member:self2‹432› = member:Foo‹434›#666; }; Cls Bar { }; new Ref(member:Bar‹433›)().self2 }; member:foo‹435›#666(2) } +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Define: +//│ defn = ClsLikeDefn: +//│ owner = N +//│ isym = object:Foo‹437› +//│ sym = member:Foo‹434› +//│ k = Obj +//│ paramsOpt = N +//│ auxParams = Nil +//│ parentPath = N +//│ methods = Ls of +//│ FunDefn: +//│ owner = S of object:Foo‹437› +//│ sym = member:self1‹431› +//│ params = Nil +//│ body = Return: +//│ res = Ref of object:Foo‹437› +//│ implct = false +//│ FunDefn: +//│ owner = S of object:Foo‹437› +//│ sym = member:self2‹432› +//│ params = Nil +//│ body = Return: +//│ res = Ref of Foo$‹451› +//│ implct = false +//│ privateFields = Nil +//│ publicFields = Nil +//│ preCtor = End of "" +//│ ctor = End of "" +//│ rest = Define: \ +//│ defn = ClsLikeDefn: +//│ owner = N +//│ isym = class:Bar‹441› +//│ sym = member:Bar‹433› +//│ k = Cls +//│ paramsOpt = N +//│ auxParams = Nil +//│ parentPath = S of Select: +//│ qual = Ref of member:Foo‹434› +//│ name = Ident of "class" +//│ methods = Nil +//│ privateFields = Nil +//│ publicFields = Nil +//│ preCtor = Return: +//│ res = Call: +//│ fun = Ref of builtin:super‹5› +//│ args = Nil +//│ implct = true +//│ ctor = End of "" +//│ rest = Define: \ +//│ defn = FunDefn: +//│ owner = N +//│ sym = member:foo‹435› +//│ params = Ls of +//│ ParamList: +//│ flags = ParamListFlags of false +//│ params = Ls of +//│ Param: +//│ flags = () +//│ sym = x‹436› +//│ sign = N +//│ restParam = N +//│ body = Assign: +//│ lhs = Foo$‹451› +//│ rhs = Call: +//│ fun = Ref of member:Foo‹434› +//│ args = Nil +//│ rest = Assign: \ +//│ lhs = $tmp‹447› +//│ rhs = Instantiate: +//│ cls = Ref of member:Bar‹433› +//│ args = Nil +//│ rest = Assign: \ +//│ lhs = $selRes‹448› +//│ rhs = Select: +//│ qual = Ref of $tmp‹447› +//│ name = Ident of "self2" +//│ rest = Assign: \ +//│ lhs = $discarded‹449› +//│ rhs = Select: +//│ qual = Ref of $tmp‹447› +//│ name = Ident of "self2$__checkNotMethod" +//│ rest = Match: \ +//│ scrut = Ref of $selRes‹448› +//│ arms = Ls of +//│ Tuple2: +//│ _1 = Lit of UnitLit of false +//│ _2 = Throw of Instantiate: +//│ cls = Select: +//│ qual = Ref of globalThis:globalThis‹0› +//│ name = Ident of "Error" +//│ args = Ls of +//│ Lit of StrLit of "Access to required field 'self2' yielded 'undefined'" +//│ dflt = N +//│ rest = Return: \ +//│ res = Ref of $selRes‹448› +//│ implct = false +//│ rest = Assign: \ +//│ lhs = $block$res‹446› +//│ rhs = Call: +//│ fun = Ref of member:foo‹435› +//│ args = Ls of +//│ Arg: +//│ spread = false +//│ value = Lit of IntLit of 2 +//│ rest = Return: \ +//│ res = Lit of UnitLit of false +//│ implct = true +//│ FAILURE: Unexpected exception +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: Foo$‹451› (class hkmc2.semantics.VarSymbol) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:50) +//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:55) +//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:92) +//│ at: scala.Option.getOrElse(Option.scala:201) +//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:94) +//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) +//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) +//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:319) +//│ at: hkmc2.codegen.js.JSBuilder.block(JSBuilder.scala:451) +//│ at: hkmc2.codegen.js.JSBuilder.body(JSBuilder.scala:454) +//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$17(JSBuilder.scala:249) +//│ at: scala.collection.immutable.List.map(List.scala:251) +//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$3(JSBuilder.scala:242) +//│ at: hkmc2.utils.Scope.nestRebindThis(Scope.scala:76) +//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:178) +//│ at: hkmc2.codegen.js.JSBuilder.worksheet(JSBuilder.scala:438) +//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:122) +//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) +//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:37) +//│ at: hkmc2.MLsDiffMaker.processTrees(MLsDiffMaker.scala:253) +//│ at: hkmc2.MLsDiffMaker.processOrigin(MLsDiffMaker.scala:216) +//│ at: hkmc2.DiffMaker.processBlock(DiffMaker.scala:212) +//│ at: hkmc2.DiffMaker.rec(DiffMaker.scala:315) +//│ at: hkmc2.DiffMaker.run(DiffMaker.scala:338) +//│ at: hkmc2.MLsDiffMaker.run(MLsDiffMaker.scala:127) +//│ at: hkmc2.JSBackendDiffMaker.run(JSBackendDiffMaker.scala:55) +//│ at: hkmc2.Watcher.go(Watcher.scala:104) +//│ at: hkmc2.Watcher.onModify(Watcher.scala:119) +//│ at: hkmc2.Watcher$$anon$1.onEvent(Watcher.scala:67) +//│ at: io.methvin.watcher.DirectoryWatcher.onEvent(DirectoryWatcher.java:425) +//│ at: io.methvin.watcher.DirectoryWatcher.runEventLoop(DirectoryWatcher.java:356) +//│ at: io.methvin.watcher.DirectoryWatcher.watch(DirectoryWatcher.java:250) +//│ at: hkmc2.Watcher.run(Watcher.scala:79) +//│ at: hkmc2.MainWatcher$.main(Watcher.scala:18) +//│ at: hkmc2.MainWatcher.main(Watcher.scala) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 229741df8..6b26ae442 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -80,6 +80,7 @@ foo(0, 2) //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 //│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' +// B is lifted but A is not, and since B extends A, it cannot access A's BlockMemberSymbol. :todo fun test(x) = class A with @@ -102,7 +103,6 @@ fun foo(x, y) = x + y + test M.foo() //│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. :expect 14 foo(10, 0) @@ -116,7 +116,6 @@ fun foo(x, y) = fun foo = M.foo() foo //│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. //│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) :expect 12 @@ -130,19 +129,28 @@ class A(x) with fun getB() = x fun getA() = M.getB() //│ ═══[WARNING] Modules are not yet lifted. -//│ ═══[WARNING] Modules are not yet lifted. :expect 2 A(2).getA() //│ = 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) = object Foo with fun self1 = this fun self2 = Foo class Bar extends Foo - new Bar.self2 + (new Bar).self2 foo(2) -//│ ═══[WARNING] Cannot yet lift the class `Bar` as it is used as a first-class class. -//│ ═══[WARNING] Cannot yet lift the class `Bar` as it is used as a first-class class. //│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: Foo$ (class hkmc2.semantics.VarSymbol) + +// `h` is lifted out, but then cannot access the BlockMemberSymbol M. +:fixme +fun f = + module M with + fun g = + fun h = M.g + h + M.g +//│ ═══[WARNING] Modules are not yet lifted. +//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) From 8ee9dcaf3241247c9d4aac297b01635e6821a64d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:37:05 +0800 Subject: [PATCH 095/127] undo line break --- .../shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index f5ec5e5e1..1251c905a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -141,8 +141,7 @@ class BlockTransformer(subst: SymbolSubst): if (own2 is fun.owner) && (sym2 is fun.sym) && (params2 is fun.params) && (body2 is fun.body) then fun else FunDefn(own2, sym2, params2, body2) - def applyDefn(defn: Defn): Defn = - defn match + def applyDefn(defn: Defn): Defn = defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(owner, k, sym, rhs) => val owner2 = owner.mapConserve(_.subst) From c73714dc10c4e6f1bbaab005bfb3629a15253bc1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:40:54 +0800 Subject: [PATCH 096/127] revert hkscratch --- hkmc2/shared/src/test/mlscript/HkScratch.mls | 210 ------------------- 1 file changed, 210 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/HkScratch.mls b/hkmc2/shared/src/test/mlscript/HkScratch.mls index bea5b397a..f46025be7 100644 --- a/hkmc2/shared/src/test/mlscript/HkScratch.mls +++ b/hkmc2/shared/src/test/mlscript/HkScratch.mls @@ -8,214 +8,4 @@ :d -:sjs -:lift -fun f(x) = - fun g = - if 2 is A do () - class A with - fun h = - fun i = x - i - 2 - 2 -//│ Elab: { ‹› fun member:f‹368›(Param(‹›,x‹369›,None), ) = { ‹› fun member:g‹367› = { if { let $scrut‹370› = 2; scrut is A -> { else () }; else () }; Cls A { ‹› fun member:h‹365› = { ‹› fun member:i‹364› = x‹369›#666; member:i‹364›#666 }; }; 2 }; 2 }; } -//│ FAILURE: Unexpected warning -//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. -//│ JS (unsanitized): -//│ let i, g, f, g$, i$; -//│ i$ = function i$(A$instance, x) { -//│ return x -//│ }; -//│ i = function i(A$instance, x) { -//│ return () => { -//│ return i$(A$instance, x) -//│ } -//│ }; -//│ g$ = function g$(x) { -//│ let A1, scrut, tmp; -//│ scrut = 2; -//│ if (scrut instanceof A1) { -//│ tmp = runtime.Unit; -//│ } else { -//│ tmp = runtime.Unit; -//│ } -//│ A1 = class A { -//│ constructor() {} -//│ get h() { -//│ let tmp1; -//│ tmp1 = i$(this, x); -//│ return tmp1; -//│ } -//│ toString() { return "A"; } -//│ }; -//│ return 2 -//│ }; -//│ g = function g(x) { -//│ return () => { -//│ return g$(x) -//│ } -//│ }; -//│ f = function f(x) { -//│ return 2 -//│ }; -//│ FAILURE: Unexpected warning -//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. -:lot -:lift -fun foo(x) = - object Foo with - fun self1 = this - fun self2 = Foo - class Bar extends Foo - (new Bar).self2 -foo(2) -//│ Elab: { ‹› fun member:foo‹435›(Param(‹›,x‹436›,None), ) = { Obj Foo { ‹module› fun member:self1‹431› = object:Foo‹437›#0; ‹module› fun member:self2‹432› = member:Foo‹434›#666; }; Cls Bar { }; new Ref(member:Bar‹433›)().self2 }; member:foo‹435›#666(2) } -//│ Lowered: -//│ Program: -//│ imports = Nil -//│ main = Define: -//│ defn = ClsLikeDefn: -//│ owner = N -//│ isym = object:Foo‹437› -//│ sym = member:Foo‹434› -//│ k = Obj -//│ paramsOpt = N -//│ auxParams = Nil -//│ parentPath = N -//│ methods = Ls of -//│ FunDefn: -//│ owner = S of object:Foo‹437› -//│ sym = member:self1‹431› -//│ params = Nil -//│ body = Return: -//│ res = Ref of object:Foo‹437› -//│ implct = false -//│ FunDefn: -//│ owner = S of object:Foo‹437› -//│ sym = member:self2‹432› -//│ params = Nil -//│ body = Return: -//│ res = Ref of Foo$‹451› -//│ implct = false -//│ privateFields = Nil -//│ publicFields = Nil -//│ preCtor = End of "" -//│ ctor = End of "" -//│ rest = Define: \ -//│ defn = ClsLikeDefn: -//│ owner = N -//│ isym = class:Bar‹441› -//│ sym = member:Bar‹433› -//│ k = Cls -//│ paramsOpt = N -//│ auxParams = Nil -//│ parentPath = S of Select: -//│ qual = Ref of member:Foo‹434› -//│ name = Ident of "class" -//│ methods = Nil -//│ privateFields = Nil -//│ publicFields = Nil -//│ preCtor = Return: -//│ res = Call: -//│ fun = Ref of builtin:super‹5› -//│ args = Nil -//│ implct = true -//│ ctor = End of "" -//│ rest = Define: \ -//│ defn = FunDefn: -//│ owner = N -//│ sym = member:foo‹435› -//│ params = Ls of -//│ ParamList: -//│ flags = ParamListFlags of false -//│ params = Ls of -//│ Param: -//│ flags = () -//│ sym = x‹436› -//│ sign = N -//│ restParam = N -//│ body = Assign: -//│ lhs = Foo$‹451› -//│ rhs = Call: -//│ fun = Ref of member:Foo‹434› -//│ args = Nil -//│ rest = Assign: \ -//│ lhs = $tmp‹447› -//│ rhs = Instantiate: -//│ cls = Ref of member:Bar‹433› -//│ args = Nil -//│ rest = Assign: \ -//│ lhs = $selRes‹448› -//│ rhs = Select: -//│ qual = Ref of $tmp‹447› -//│ name = Ident of "self2" -//│ rest = Assign: \ -//│ lhs = $discarded‹449› -//│ rhs = Select: -//│ qual = Ref of $tmp‹447› -//│ name = Ident of "self2$__checkNotMethod" -//│ rest = Match: \ -//│ scrut = Ref of $selRes‹448› -//│ arms = Ls of -//│ Tuple2: -//│ _1 = Lit of UnitLit of false -//│ _2 = Throw of Instantiate: -//│ cls = Select: -//│ qual = Ref of globalThis:globalThis‹0› -//│ name = Ident of "Error" -//│ args = Ls of -//│ Lit of StrLit of "Access to required field 'self2' yielded 'undefined'" -//│ dflt = N -//│ rest = Return: \ -//│ res = Ref of $selRes‹448› -//│ implct = false -//│ rest = Assign: \ -//│ lhs = $block$res‹446› -//│ rhs = Call: -//│ fun = Ref of member:foo‹435› -//│ args = Ls of -//│ Arg: -//│ spread = false -//│ value = Lit of IntLit of 2 -//│ rest = Return: \ -//│ res = Lit of UnitLit of false -//│ implct = true -//│ FAILURE: Unexpected exception -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: Foo$‹451› (class hkmc2.semantics.VarSymbol) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:50) -//│ at: hkmc2.InternalError$.apply(Diagnostic.scala:55) -//│ at: hkmc2.utils.Scope.lookup_$bang$$anonfun$1(Scope.scala:92) -//│ at: scala.Option.getOrElse(Option.scala:201) -//│ at: hkmc2.utils.Scope.lookup_$bang(Scope.scala:94) -//│ at: hkmc2.codegen.js.JSBuilder.getVar(JSBuilder.scala:77) -//│ at: hkmc2.codegen.js.JSBuilder.result(JSBuilder.scala:96) -//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:319) -//│ at: hkmc2.codegen.js.JSBuilder.block(JSBuilder.scala:451) -//│ at: hkmc2.codegen.js.JSBuilder.body(JSBuilder.scala:454) -//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$17(JSBuilder.scala:249) -//│ at: scala.collection.immutable.List.map(List.scala:251) -//│ at: hkmc2.codegen.js.JSBuilder.$anonfun$3(JSBuilder.scala:242) -//│ at: hkmc2.utils.Scope.nestRebindThis(Scope.scala:76) -//│ at: hkmc2.codegen.js.JSBuilder.returningTerm(JSBuilder.scala:178) -//│ at: hkmc2.codegen.js.JSBuilder.worksheet(JSBuilder.scala:438) -//│ at: hkmc2.JSBackendDiffMaker.processTerm(JSBackendDiffMaker.scala:122) -//│ at: hkmc2.BbmlDiffMaker.processTerm(BbmlDiffMaker.scala:36) -//│ at: hkmc2.LlirDiffMaker.processTerm(LlirDiffMaker.scala:37) -//│ at: hkmc2.MLsDiffMaker.processTrees(MLsDiffMaker.scala:253) -//│ at: hkmc2.MLsDiffMaker.processOrigin(MLsDiffMaker.scala:216) -//│ at: hkmc2.DiffMaker.processBlock(DiffMaker.scala:212) -//│ at: hkmc2.DiffMaker.rec(DiffMaker.scala:315) -//│ at: hkmc2.DiffMaker.run(DiffMaker.scala:338) -//│ at: hkmc2.MLsDiffMaker.run(MLsDiffMaker.scala:127) -//│ at: hkmc2.JSBackendDiffMaker.run(JSBackendDiffMaker.scala:55) -//│ at: hkmc2.Watcher.go(Watcher.scala:104) -//│ at: hkmc2.Watcher.onModify(Watcher.scala:119) -//│ at: hkmc2.Watcher$$anon$1.onEvent(Watcher.scala:67) -//│ at: io.methvin.watcher.DirectoryWatcher.onEvent(DirectoryWatcher.java:425) -//│ at: io.methvin.watcher.DirectoryWatcher.runEventLoop(DirectoryWatcher.java:356) -//│ at: io.methvin.watcher.DirectoryWatcher.watch(DirectoryWatcher.java:250) -//│ at: hkmc2.Watcher.run(Watcher.scala:79) -//│ at: hkmc2.MainWatcher$.main(Watcher.scala:18) -//│ at: hkmc2.MainWatcher.main(Watcher.scala) From d4cd4694859fe0db31bc62f89f7c7d5d709b09c6 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:45:54 +0800 Subject: [PATCH 097/127] BlockTransformerNoRec change --- .../hkmc2/codegen/BlockTransformer.scala | 70 +++++++------------ .../src/test/mlscript/lifter/ClassInClass.mls | 10 ++- 2 files changed, 33 insertions(+), 47 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 1251c905a..ce2f75b1d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -13,6 +13,8 @@ class BlockTransformer(subst: SymbolSubst): given SymbolSubst = subst + def applySubBlock(b: Block): Block = applyBlock(b) + def applyBlock(b: Block): Block = b match case _: End => b case Break(lbl) => @@ -34,43 +36,43 @@ class BlockTransformer(subst: SymbolSubst): val scrut2 = applyPath(scrut) val arms2 = arms.mapConserve: arm => val cse2 = applyCase(arm._1) - val blk2 = applyBlock(arm._2) + val blk2 = applySubBlock(arm._2) if (cse2 is arm._1) && (blk2 is arm._2) then arm else (cse2, blk2) - val dflt2 = dflt.mapConserve(applyBlock) - val rst2 = applyBlock(rst) + val dflt2 = dflt.mapConserve(applySubBlock) + val rst2 = applySubBlock(rst) if (scrut2 is scrut) && (arms2 is arms) && (dflt2 is dflt) && (rst2 is rst) then b else Match(scrut2, arms2, dflt2, rst2) case Label(lbl, bod, rst) => val lbl2 = applyLocal(lbl) - val bod2 = applyBlock(bod) - val rst2 = applyBlock(rst) + val bod2 = applySubBlock(bod) + val rst2 = applySubBlock(rst) if (lbl2 is lbl) && (bod2 is bod) && (rst2 is rst) then b else Label(lbl2, bod2, rst2) case Begin(sub, rst) => - val sub2 = applyBlock(sub) - val rst2 = applyBlock(rst) + val sub2 = applySubBlock(sub) + val rst2 = applySubBlock(rst) if (sub2 is sub) && (rst2 is rst) then b else Begin(sub2, rst2) case TryBlock(sub, fin, rst) => - val sub2 = applyBlock(sub) - val fin2 = applyBlock(fin) - val rst2 = applyBlock(rst) + val sub2 = applySubBlock(sub) + val fin2 = applySubBlock(fin) + val rst2 = applySubBlock(rst) if (sub2 is sub) && (fin2 is fin) && (rst2 is rst) then b else TryBlock(sub2, fin2, rst2) case Assign(l, r, rst) => applyResult2(r): r2 => val l2 = applyLocal(l) - val rst2 = applyBlock(rst) + val rst2 = applySubBlock(rst) if (l2 is l) && (r2 is r) && (rst2 is rst) then b else Assign(l2, r2, rst2) case b @ AssignField(l, n, r, rst) => applyResult2(r): r2 => val l2 = applyPath(l) - val rst2 = applyBlock(rst) + val rst2 = applySubBlock(rst) val sym = b.symbol.mapConserve(_.subst) if (l2 is l) && (r2 is r) && (rst2 is rst) && (sym is b.symbol) then b else AssignField(l2, n, r2, rst2)(sym) case Define(defn, rst) => val defn2 = applyDefn(defn) - val rst2 = applyBlock(rst) + val rst2 = applySubBlock(rst) if (defn2 is defn) && (rst2 is rst) then b else Define(defn2, rst2) case HandleBlock(l, res, par, args, cls, hdr, bod, rst) => val l2 = applyLocal(l) @@ -79,8 +81,8 @@ class BlockTransformer(subst: SymbolSubst): val args2 = args.mapConserve(applyPath) val cls2 = cls.subst val hdr2 = hdr.mapConserve(applyHandler) - val bod2 = applyBlock(bod) - val rst2 = applyBlock(rst) + val bod2 = applySubBlock(bod) + val rst2 = applySubBlock(rst) if (l2 is l) && (res2 is res) && (par2 is par) && (args2 is args) && (cls2 is cls) && (hdr2 is hdr) && (bod2 is bod) && (rst2 is rst) then b else HandleBlock(l2, res2, par2, args2, cls2, hdr2, bod2, rst2) @@ -88,7 +90,7 @@ class BlockTransformer(subst: SymbolSubst): applyResult2(rhs): rhs2 => val lhs2 = applyPath(lhs) val fld2 = applyPath(fld) - val rest2 = applyBlock(rest) + val rest2 = applySubBlock(rest) if (lhs2 is lhs) && (fld2 is fld) && (rhs2 is rhs) && (rest2 is rest) then b else AssignDynField(lhs2, fld2, arrayIdx, rhs2, rest2) @@ -137,7 +139,7 @@ class BlockTransformer(subst: SymbolSubst): val own2 = fun.owner.mapConserve(_.subst) val sym2 = fun.sym.subst val params2 = fun.params.mapConserve(applyParamList) - val body2 = applyBlock(fun.body) + val body2 = applySubBlock(fun.body) if (own2 is fun.owner) && (sym2 is fun.sym) && (params2 is fun.params) && (body2 is fun.body) then fun else FunDefn(own2, sym2, params2, body2) @@ -160,8 +162,8 @@ class BlockTransformer(subst: SymbolSubst): val methods2 = methods.mapConserve(applyFunDefn) val privateFields2 = privateFields.mapConserve(_.subst) val publicFields2 = publicFields.mapConserve(applyTermDefinition) - val preCtor2 = applyBlock(preCtor) - val ctor2 = applyBlock(ctor) + val preCtor2 = applySubBlock(preCtor) + val ctor2 = applySubBlock(ctor) if (own2 is own) && (isym2 is isym) && (sym2 is sym) && (paramsOpt2 is paramsOpt) && (auxParams2 is auxParams) && @@ -198,14 +200,14 @@ class BlockTransformer(subst: SymbolSubst): val sym2 = hdr.sym.subst val resumeSym2 = hdr.resumeSym.subst val params2 = hdr.params.mapConserve(applyParamList) - val body2 = applyBlock(hdr.body) + val body2 = applySubBlock(hdr.body) if (sym2 is hdr.sym) && (resumeSym2 is hdr.resumeSym) && (params2 is hdr.params) && (body2 is hdr.body) then hdr else Handler(sym2, resumeSym2, params2, body2) def applyLam(lam: Value.Lam): Value.Lam = val params2 = applyParamList(lam.params) - val body2 = applyBlock(lam.body) + val body2 = applySubBlock(lam.body) if (params2 is lam.params) && (body2 is lam.body) then lam else Value.Lam(params2, body2) def applyTermDefinition(td: TermDefinition): TermDefinition = @@ -234,7 +236,7 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst val args2 = args.mapConserve(applyPath) val cls2 = cls.subst val hdr2 = hdr.mapConserve(applyHandler) - val rst2 = applyBlock(rst) + val rst2 = applySubBlock(rst) if (l2 is l) && (res2 is res) && (par2 is par) && (args2 is args) && (cls2 is cls) && (hdr2 is hdr) && (rst2 is rst) then b else HandleBlock(l2, res2, par2, args2, cls2, hdr2, bod, rst2) @@ -242,26 +244,4 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst // does not traverse into any other block class BlockTransformerNoRec(subst: SymbolSubst) extends BlockTransformerShallow(subst): - override def applyBlock(b: Block): Block = b match - case Match(scrut, arms, dflt, rest) => - val scrut2 = applyPath(scrut) - val arms2 = arms.mapConserve: arm => - val cse2 = applyCase(arm._1) - if (cse2 is arm._1) then arm else (cse2, arm._2) - if (scrut is scrut2) then b else Match(scrut2, arms, dflt, rest) - case Assign(lhs, rhs, rest) => - applyResult2(rhs): rhs2 => - val lhs2 = lhs.subst - if (lhs is lhs2) && (rhs is rhs2) then b else Assign(lhs2, rhs2, rest) - case AssignField(lhs, ident, rhs, rest) => - applyResult2(rhs): rhs2 => - val lhs2 = applyPath(lhs) - if rhs is rhs2 then b else AssignField(lhs2, ident, rhs2, rest)(N) - case AssignDynField(l, fld, arrayIdx, r, rst) => - applyResult2(r): r2 => - val l2 = applyPath(l) - val fld2 = applyPath(fld) - if (l2 is l) && (fld2 is fld) && (r2 is r) - then b else AssignDynField(l2, fld2, arrayIdx, r2, rst) - case _: Label | _: Begin | _: TryBlock | _: Define | _: HandleBlock | _: End => b - case _: Return | _: Break | _: Continue | _: HandleBlockReturn | _: Throw => super.applyBlock(b) + override def applySubBlock(b: Block): Block = b diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls index 316596e23..d84d21550 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls @@ -123,8 +123,9 @@ class A with val x = fun g() = 2 g +(new A).x() //│ JS (unsanitized): -//│ let g, A5, g$; +//│ let g, A5, tmp2, g$; //│ g$ = function g$(A$instance) { //│ return 2 //│ }; @@ -135,7 +136,12 @@ class A with //│ }; //│ A5 = class A4 { //│ constructor() { -//│ this.x = g; +//│ let g$this; +//│ g$this = runtime.safeCall(g(this)); +//│ this.x = g$this; //│ } //│ toString() { return "A"; } //│ }; +//│ tmp2 = new A5(); +//│ runtime.safeCall(tmp2.x()) +//│ = 2 From b4092a267b0201366e3a9189b4bcaa0cda6f0c9b Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:46:05 +0800 Subject: [PATCH 098/127] rename test and fix test --- .../test/mlscript/lifter/{ClassInClass.mls => DefnsInClass.mls} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename hkmc2/shared/src/test/mlscript/lifter/{ClassInClass.mls => DefnsInClass.mls} (100%) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls similarity index 100% rename from hkmc2/shared/src/test/mlscript/lifter/ClassInClass.mls rename to hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls From 09996d80ac3c3862faafb7c3cbbe2d123b814a0a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:57:30 +0800 Subject: [PATCH 099/127] move lambda rewriter to new class --- .../scala/hkmc2/codegen/LambdaRewriter.scala | 33 +++++++++++++++++++ .../src/main/scala/hkmc2/codegen/Lifter.scala | 26 +-------------- .../main/scala/hkmc2/codegen/Lowering.scala | 26 +-------------- .../mlscript/basics/BadMemberProjections.mls | 6 ++-- .../src/test/mlscript/codegen/Hygiene.mls | 4 +-- 5 files changed, 40 insertions(+), 55 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala new file mode 100644 index 000000000..d089b7345 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -0,0 +1,33 @@ +package hkmc2 + +import mlscript.utils.*, shorthands.* +import utils.* + +import hkmc2.codegen.* +import hkmc2.semantics.* +import semantics.Elaborator.State + +class LambdaRewriter(using State): + def desugar(b: Block) = + def rewriteOneBlk(b: Block) = + var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil + val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): + override def applyValue(v: Value): Value = v match + case lam: Value.Lam => + val sym = BlockMemberSymbol("lambda", Nil) + lambdasList ::= (sym -> super.applyLam(lam)) + Value.Ref(sym) + case _ => super.applyValue(v) + val blk = lambdaRewriter.applyBlock(b) + (blk, lambdasList) + + val transformer = new BlockTransformer(SymbolSubst()): + override def applyBlock(b: Block): Block = + val (newBlk, lambdasList) = rewriteOneBlk(b) + val lambdaDefns = lambdasList.map: + case (sym, Value.Lam(params, body)) => + FunDefn(None, sym, params :: Nil, body) + val ret = lambdaDefns.foldLeft(newBlk): + case (acc, defn) => Define(defn, acc) + super.applyBlock(ret) + transformer.applyBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index bd077d5a1..bd402a62e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -959,35 +959,11 @@ class Lifter(using State, Raise): end liftDefnsInFn - def desugarLambdas(b: Block) = - def rewriteOneBlk(b: Block) = - var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil - val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): - override def applyValue(v: Value): Value = v match - case lam: Value.Lam => - val sym = BlockMemberSymbol("lambda", Nil) - lambdasList ::= (sym -> super.applyLam(lam)) - Value.Ref(sym) - case _ => super.applyValue(v) - val blk = lambdaRewriter.applyBlock(b) - (blk, lambdasList) - - val transformer = new BlockTransformer(SymbolSubst()): - override def applyBlock(b: Block): Block = - val (newBlk, lambdasList) = rewriteOneBlk(b) - val lambdaDefns = lambdasList.map: - case (sym, Value.Lam(params, body)) => - FunDefn(None, sym, params :: Nil, body) - val ret = lambdaDefns.foldLeft(newBlk): - case (acc, defn) => Define(defn, acc) - super.applyBlock(ret) - transformer.applyBlock(b) - // top-level def transform(b: 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 = desugarLambdas(b) + val blk = new LambdaRewriter().desugar(b) val analyzer = UsedVarAnalyzer(blk) val ctx = LifterCtx diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 01d03b8cf..696e8bd36 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -584,32 +584,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx): // def topLevel(t: st): Block = // subTerm(t)(r => codegen.Assign(resSym, r, codegen.End()))(using Subst.empty) - def desugarLambdas(b: Block) = - def rewriteOneBlk(b: Block) = - var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil - val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): - override def applyValue(v: Value): Value = v match - case lam: Value.Lam => - val sym = BlockMemberSymbol("lambda", Nil) - lambdasList ::= (sym -> super.applyLam(lam)) - Value.Ref(sym) - case _ => super.applyValue(v) - val blk = lambdaRewriter.applyBlock(b) - (blk, lambdasList) - - val transformer = new BlockTransformer(SymbolSubst()): - override def applyBlock(b: Block): Block = - val (newBlk, lambdasList) = rewriteOneBlk(b) - val lambdaDefns = lambdasList.map: - case (sym, Value.Lam(params, body)) => - FunDefn(None, sym, params :: Nil, body) - val ret = lambdaDefns.foldLeft(newBlk): - case (acc, defn) => Define(defn, acc) - super.applyBlock(ret) - transformer.applyBlock(b) - def topLevel(t: st): Block = - val res = desugarLambdas(term(t)(ImplctRet)(using Subst.empty)) + val res = new LambdaRewriter().desugar(term(t)(ImplctRet)(using Subst.empty)) val stackSafe = config.stackSafety match case N => res case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index cc049f619..9127d26f4 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -22,7 +22,7 @@ :re 1::x() //│ ╔══[ERROR] Integer literal is not a known class. -//│ ║ l.23: 1::x() +//│ ║ l.19: 1::x() //│ ║ ^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -52,7 +52,7 @@ let x = 1 :e "A"::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.53: "A"::x +//│ ║ l.49: "A"::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -61,7 +61,7 @@ let x = 1 :e "A" ::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.62: "A" ::x +//│ ║ l.58: "A" ::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 58e316ba8..8fdf6a3e8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -73,10 +73,10 @@ module Test with val x = 1 let x = 2 //│ ╔══[ERROR] Name 'x' is already used -//│ ║ l.74: let x = 2 +//│ ║ l.67: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.73: val x = 1 +//│ ║ l.66: val x = 1 //│ ╙── ^^^^^ //│ JS: //│ Test4 = class Test3 { From 8dc097a990e800b1abb7d966f37ebee7f898b2bb Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 15:58:30 +0800 Subject: [PATCH 100/127] change to object --- .../shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala | 4 ++-- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 2 +- hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala index d089b7345..feff77ebd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -7,8 +7,8 @@ import hkmc2.codegen.* import hkmc2.semantics.* import semantics.Elaborator.State -class LambdaRewriter(using State): - def desugar(b: Block) = +object LambdaRewriter: + def desugar(b: Block)(using State) = def rewriteOneBlk(b: Block) = var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index bd402a62e..0b7089b92 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -963,7 +963,7 @@ class Lifter(using State, Raise): def transform(b: 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 = new LambdaRewriter().desugar(b) + val blk = LambdaRewriter.desugar(b) val analyzer = UsedVarAnalyzer(blk) val ctx = LifterCtx diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 696e8bd36..fda19523f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -585,7 +585,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): // subTerm(t)(r => codegen.Assign(resSym, r, codegen.End()))(using Subst.empty) def topLevel(t: st): Block = - val res = new LambdaRewriter().desugar(term(t)(ImplctRet)(using Subst.empty)) + val res = LambdaRewriter.desugar(term(t)(ImplctRet)(using Subst.empty)) val stackSafe = config.stackSafety match case N => res case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) From 359397e33ca537cce200042bb2557f9c80353540 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 16:02:26 +0800 Subject: [PATCH 101/127] address first batch of changes --- .../src/main/scala/hkmc2/codegen/Block.scala | 6 ++-- .../src/main/scala/hkmc2/codegen/Lifter.scala | 28 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 4a99a937c..96826da79 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -56,7 +56,6 @@ sealed abstract class Block extends Product with AutoLocated: case TryBlock(sub, fin, rst) => 1 + sub.size + fin.size + rst.size case Label(_, bod, rst) => 1 + bod.size + rst.size case HandleBlock(lhs, res, par, args, cls, handlers, bdy, rst) => 1 + handlers.map(_.body.size).sum + bdy.size + rst.size - case AssignDynField(lhs, fld, arrayIdx, rhs, rst) => 1 + rst.size // TODO conserve if no changes def mapTail(f: BlockTail => Block): Block = this match @@ -149,8 +148,9 @@ sealed abstract class Block extends Product with AutoLocated: // last. This is so that using defns.foldLeft later to add the definitions to the front of a block, // we don't need to reverse the list again to preserve the order of the definitions. def floatOutDefns( - ignore: Defn => Bool = _ => false, - preserve: Defn => Bool = _ => false) = + ignore: Defn => Bool = _ => false, + preserve: Defn => Bool = _ => false + ) = var defns: List[Defn] = Nil val transformer = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 0b7089b92..68d80086c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -10,15 +10,12 @@ import hkmc2.semantics.Elaborator.State import hkmc2.syntax.Tree import hkmc2.codegen.llir.FreshInt -import scala.collection.mutable.ListBuffer -import scala.collection.mutable.LinkedHashSet import scala.collection.mutable.LinkedHashMap import scala.collection.mutable.Map as MutMap -import scala.collection.mutable.Set as MutSet object Lifter: /** - * Describes the free variables of a function that have been accessed by nested definitions. + * 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. * @param reqCapture The free variables that must be captured using a heap-allocated object. */ @@ -28,7 +25,7 @@ object Lifter: val empty = FreeVars(Set.empty, Set.empty) /** - * Describes the free variables of a function that have been accessed by nested definitions. + * Describes the free variables of functions that have been accessed by their nested definitions. * @param mp The map from functions' `BlockMemberSymbol`s to their accessed variables. */ class UsedLocalsMap(val mp: Map[BlockMemberSymbol, FreeVars]): @@ -37,10 +34,7 @@ object Lifter: case fn -> vars => vars.vars.map(v => v -> fn) // gets the function to which a local belongs def lookup(l: Local) = inverse.get(l) - - object AccessInfo: - val empty = AccessInfo(Set.empty, Set.empty, Set.empty) - + /** * Describes previously defined locals and definitions which could possibly be accessed or mutated by a definition. * @@ -49,9 +43,10 @@ object Lifter: * @param refdDefns Previously defined definitions which could possibly be used by this definition. */ case class AccessInfo( - accessed: Set[Local], - mutated: Set[Local], - refdDefns: Set[BlockMemberSymbol]): + accessed: Set[Local], + mutated: Set[Local], + refdDefns: Set[BlockMemberSymbol] + ): def ++(that: AccessInfo) = AccessInfo( accessed ++ that.accessed, mutated ++ that.mutated, @@ -77,9 +72,12 @@ object Lifter: mutated, refdDefns.intersect(locals) ) - def addAccess(l: Local) = this.copy(accessed = accessed + l) - def addMutated(l: Local) = this.copy(accessed = accessed + l, mutated = mutated + l) - def addRefdDefn(l: BlockMemberSymbol) = this.copy(refdDefns = refdDefns + l) + 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) + + object AccessInfo: + val empty = AccessInfo(Set.empty, Set.empty, Set.empty) def getVars(d: Defn)(using state: State): Set[Local] = d match case f: FunDefn => From fc8fe8b48aef6cb0c2c6c47f6ccbcc58d0a5062f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 16:26:11 +0800 Subject: [PATCH 102/127] pr comments --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 149 +++++++++--------- .../src/test/mlscript/backlog/Lifter.mls | 10 +- .../mlscript/basics/BadMemberProjections.mls | 6 +- .../src/test/mlscript/codegen/Hygiene.mls | 4 +- .../src/test/mlscript/lifter/ClassInFun.mls | 14 +- 5 files changed, 90 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 68d80086c..7c38dbfe0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -36,7 +36,9 @@ object Lifter: def lookup(l: Local) = inverse.get(l) /** - * Describes previously defined locals and definitions which could possibly be accessed or mutated by a definition. + * Describes previously defined locals and definitions which could possibly be accessed or mutated by particular definition. + * Here, a "previously defined" local or definition means it is accessible to the particular definition (which we call `d`), + * but is not defined *by* `d`. * * @param accessed Previously defined locals which could possibly be accessed or mutated. * @param mutated Such locals which could also be mutated by this definition. @@ -125,7 +127,7 @@ class Lifter(using State, Raise): import Lifter.* /** - * The context of the class lifter. One can create an empty context using `Lifter.empty`. + * 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. @@ -145,9 +147,9 @@ class Lifter(using State, Raise): * @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 replDefns Ignored definitions whose definitions we should replace in the block itself + * @param replacedDefns Ignored definitions that have been rewritten and need to be replaced at the definition site. */ - case class LifterCtx( + 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, @@ -165,7 +167,7 @@ class Lifter(using State, Raise): val ignoredBmsPaths: Map[BlockMemberSymbol, Path] = Map.empty, val localPaths: Map[Local, Local] = Map.empty, val isymPaths: Map[InnerSymbol, Local] = Map.empty, - val replDefns: Map[BlockMemberSymbol, Defn] = Map.empty, + val replacedDefns: Map[BlockMemberSymbol, Defn] = Map.empty, ): // gets the function to which a local belongs def lookup(l: Local) = usedLocals.lookup(l) @@ -203,7 +205,7 @@ class Lifter(using State, Raise): def addIgnoredBmsPaths(m: Map[BlockMemberSymbol, Path]) = copy(ignoredBmsPaths = ignoredBmsPaths ++ m) def addIsymPath(isym: InnerSymbol, l: Local) = copy(isymPaths = isymPaths + (isym -> l)) def addIsymPaths(mp: Map[InnerSymbol, Local]) = copy(isymPaths = isymPaths ++ mp) - def addReplDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(replDefns = replDefns ++ mp) + def addreplacedDefns(mp: Map[BlockMemberSymbol, Defn]) = copy(replacedDefns = replacedDefns ++ mp) def inModule(defn: ClsLikeDefn) = copy(curModules = defn :: curModules) def flushModules = // called when we are lifted out while in some modules, so we need to add the modules' isym paths @@ -225,7 +227,7 @@ class Lifter(using State, Raise): val nme = f.sym.nme + "$capture" val clsSym = ClassSymbol( - Tree.TypeDef(syntax.Cls, Tree.Error(), N, N), + Tree.DummyTypeDef(syntax.Cls), Tree.Ident(nme) ) @@ -234,9 +236,9 @@ class Lifter(using State, Raise): val fresh = FreshInt() val varsMap: Map[Local, TermSymbol] = cap.map: s => - val id = fresh.make - s -> TermSymbol(syntax.ParamBind, S(clsSym), Tree.Ident(s.nme + id + "$")) - .toMap + val id = fresh.make + s -> TermSymbol(syntax.ParamBind, S(clsSym), Tree.Ident(s.nme + id + "$")) + .toMap val varsList = cap.toList @@ -260,16 +262,13 @@ class Lifter(using State, Raise): val sym = c match case f: FunDefn => f.sym case c: ClsLikeDefn => c.isym - case _ => c.sym // unreachable + 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.get(sym) match - case Some(value) => value - case None => - val ret: Set[Local] = c.freeVars.collect: - case s: InnerSymbol => s - case t: TermSymbol if t.owner.isDefined => t.owner.get - innerSymCache.addOne(sym -> ret) - ret + innerSymCache.getOrElseUpdate(sym, create) /** * Determines whether a certain class's `this` needs to be captured by a class being lifted. @@ -376,7 +375,7 @@ class Lifter(using State, Raise): clsSymToBms.get(cls) match case Some(value) if !ignored.contains(value) => // don't generate a warning if it's already ignored raise(WarningReport( - msg"Cannot yet lift the class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, + msg"Cannot yet lift class/module `${value.nme}` as it is used in an instance check." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += value @@ -428,7 +427,7 @@ class Lifter(using State, Raise): override def applyValue(v: Value): Value = v match case RefOfBms(l) if clsSyms.contains(l) && !modOrObj(ctx.defns(l)) => raise(WarningReport( - msg"Cannot yet lift the class `${l.nme}` as it is used as a first-class class." -> N :: Nil, + msg"Cannot yet lift class `${l.nme}` as it is used as a first-class class." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += l @@ -538,7 +537,7 @@ class Lifter(using State, Raise): blockBuilder .assign(sym, Call(d.sym.asPath, Nil)(true, false)) .rest(applyBlock(rest)) - case _ => ctx.replDefns.get(d.sym) match + case _ => ctx.replacedDefns.get(d.sym) match case Some(value) => Define(value, applyBlock(rest)) case None => super.applyBlock(b) @@ -695,26 +694,26 @@ class Lifter(using State, Raise): val extraParamsCaptures = capturesSymbols.map: // parameter list case (d, sym) => Param(FldFlags.empty, sym, None) val newCapturePaths = capturesSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym.asPath - .toMap + case (d, sym) => d -> sym.asPath + .toMap val extraParamsLocals = localsSymbols.map: // parameter list case (d, sym) => Param(FldFlags.empty, sym, None) val newLocalsPaths = localsSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym - .toMap + case (d, sym) => d -> sym + .toMap val extraParamsIsyms = isymSymbols.map: // parameter list case (d, sym) => Param(FldFlags.empty, sym, None) val newIsymPaths = isymSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym - .toMap + case (d, sym) => d -> sym + .toMap val extraParamsBms = bmsSymbols.map: // parameter list case (d, sym) => Param(FldFlags.empty, sym, None) val newBmsPaths = bmsSymbols.map: // mapping from sym to param symbol - case (d, sym) => d -> sym.asPath - .toMap + case (d, sym) => d -> sym.asPath + .toMap val extraParams = extraParamsBms ++ extraParamsIsyms ++ extraParamsLocals ++ extraParamsCaptures @@ -816,7 +815,7 @@ class Lifter(using State, Raise): val newK = c.k match case syntax.Mod => syntax.Mod case syntax.Obj => syntax.Cls - case _ => c.k // unreachable + case _ => wat("unreachable", c.k) val newDef = c.copy( k = newK, paramsOpt = N, @@ -836,13 +835,13 @@ class Lifter(using State, Raise): val (ctorIgnored, ctorIncluded) = allCtorDefns.partition(d => ctxx.ignored(d.sym)) val nestedClsPaths: Map[Local, Local] = ctorIncluded.map: - case c: ClsLikeDefn if modOrObj(c) => ctxx.modLocals.get(c.sym) match - case Some(sym) => S(c.sym -> sym) - case _ => S(c.sym -> c.sym) - case _ => None - .collect: - case Some(x) => x - .toMap + case c: ClsLikeDefn if modOrObj(c) => ctxx.modLocals.get(c.sym) match + case Some(sym) => S(c.sym -> sym) + case _ => S(c.sym -> c.sym) + case _ => None + .collect: + case Some(x) => x + .toMap val newCtx_ = ctxx .addLocalPaths(nestedClsPaths) @@ -865,9 +864,9 @@ class Lifter(using State, Raise): lifted.liftedDefn.sym -> lifted.liftedDefn .toMap - val replDefnsCtx = newCtx.addReplDefns(ctorIgnoredRewrite) - val newPreCtor = rewriteBlk(preCtor, S(c), replDefnsCtx) - val newCtor = rewriteBlk(ctor, S(c), replDefnsCtx) + val replacedDefnsCtx = newCtx.addreplacedDefns(ctorIgnoredRewrite) + val newPreCtor = rewriteBlk(preCtor, S(c), replacedDefnsCtx) + val newCtor = rewriteBlk(ctor, S(c), replacedDefnsCtx) val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) @@ -940,7 +939,7 @@ class Lifter(using State, Raise): lifted.liftedDefn.sym -> lifted.liftedDefn .toMap - val transformed = rewriteBlk(blk, N, captureCtx.addReplDefns(ignoredRewrite)) + val transformed = rewriteBlk(blk, N, captureCtx.addreplacedDefns(ignoredRewrite)) if thisVars.reqCapture.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) @@ -987,16 +986,16 @@ class Lifter(using State, Raise): walker.applyBlock(b) val modLocals = (modules ++ objects).map: c => - analyzer.nestedIn.get(c.sym) match - case Some(bms) => - val nestedIn = analyzer.defnsMap(bms) - nestedIn match - 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 + analyzer.nestedIn.get(c.sym) match + case Some(bms) => + val nestedIn = analyzer.defnsMap(bms) + nestedIn match + 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 val ctxx = ctx .addIgnored(unliftable) @@ -1166,27 +1165,25 @@ class UsedVarAnalyzer(b: Block)(using State): * @param defn The definition to search through. * @return The variables which this definition could possibly mutate. */ - private def findAccessesShallow(defn: Defn): AccessInfo = accessedCache.get(defn.sym) match - case Some(value) => value - case None => - val ret = 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.addOne(defn.sym -> ret) - ret + 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] = @@ -1266,9 +1263,9 @@ class UsedVarAnalyzer(b: Block)(using State): private def reqdCaptureLocals(f: FunDefn) = var (_, defns) = f.body.floatOutDefns() val defnSyms = defns.collect: - case f: FunDefn => f.sym -> f - case c: ClsLikeDefn => c.sym -> c - .toMap + case f: FunDefn => f.sym -> f + case c: ClsLikeDefn => c.sym -> c + .toMap val thisVars = definedLocals(f.sym) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 6b26ae442..3da716db8 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -48,7 +48,7 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 1 @@ -58,21 +58,21 @@ fun foo(x, n) = class C() let res = if x is C then "Y" else "N" if n > 0 then res + foo(C(), n - 1) else "" -//│ ═══[WARNING] Cannot yet lift the class/module `C` as it is used in an instance check. +//│ ═══[WARNING] Cannot yet lift class/module `C` as it is used in an instance check. :todo // should return the function that creates C instances :w fun foo() = class C() C -//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :w fun foo() = class C() fun f = C() C -//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :todo :expect "NN" @@ -89,7 +89,7 @@ fun test(x) = 0 is A B().get test(2) -//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. //│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) /// The following are related to modules and objects. /// diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index 9127d26f4..cc049f619 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -22,7 +22,7 @@ :re 1::x() //│ ╔══[ERROR] Integer literal is not a known class. -//│ ║ l.19: 1::x() +//│ ║ l.23: 1::x() //│ ║ ^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -52,7 +52,7 @@ let x = 1 :e "A"::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.49: "A"::x +//│ ║ l.53: "A"::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -61,7 +61,7 @@ let x = 1 :e "A" ::x //│ ╔══[ERROR] String literal is not a known class. -//│ ║ l.58: "A" ::x +//│ ║ l.62: "A" ::x //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 8fdf6a3e8..58e316ba8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -73,10 +73,10 @@ module Test with val x = 1 let x = 2 //│ ╔══[ERROR] Name 'x' is already used -//│ ║ l.67: let x = 2 +//│ ║ l.74: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.66: val x = 1 +//│ ║ l.73: val x = 1 //│ ╙── ^^^^^ //│ JS: //│ Test4 = class Test3 { diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 69a1f2436..ff02416f7 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -144,7 +144,7 @@ fun f(used1, unused1) = let foo = Test foo(unused1) f(1, 2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 1 :expect 1 @@ -446,14 +446,14 @@ fun foo(x, n) = class C() let res = if x is C then "Y" else "N" if n > 0 then res + foo(C(), n - 1) else "" -//│ ═══[WARNING] Cannot yet lift the class/module `C` as it is used in an instance check. +//│ ═══[WARNING] Cannot yet lift class/module `C` as it is used in an instance check. :todo :w fun foo() = class C() C -//│ ═══[WARNING] Cannot yet lift the class `C` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. :todo :expect "NN" @@ -468,7 +468,7 @@ fun f() = fun foo() = Test foo()(0) f().get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 0 :sjs @@ -482,7 +482,7 @@ fun f(x) = let foo = Test foo() f(2).get() -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ JS (unsanitized): //│ let h4, f10, tmp13, h$4; //│ h$4 = function h$(Test$instance, x) { @@ -510,7 +510,7 @@ f(2).get() //│ }; //│ tmp13 = f10(2); //│ runtime.safeCall(tmp13.get()) -//│ ═══[WARNING] Cannot yet lift the class `Test` as it is used as a first-class class. +//│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 2 :fixme @@ -549,5 +549,5 @@ fun test(x) = 0 is A B().get test(2) -//│ ═══[WARNING] Cannot yet lift the class/module `A` as it is used in an instance check. +//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. //│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) From 9efdd8e7c20f7da104e109c91160b9f4b5e1d104 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 16:28:57 +0800 Subject: [PATCH 103/127] update comment --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 7c38dbfe0..f78d77ab9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -147,7 +147,7 @@ class Lifter(using State, Raise): * @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 definitions that have been rewritten and need to be replaced at the definition site. + * @param replacedDefns Ignored (unlifted) definitions that have been rewritten and need to be replaced at the definition site. */ case class LifterCtx private ( val defns: Map[BlockMemberSymbol, Defn] = Map.empty, From 4dff1eee6cb375112fbbb04c10c00df4163bf8d0 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 17:02:05 +0800 Subject: [PATCH 104/127] BlockTraverser --- .../hkmc2/codegen/BlockTransformer.scala | 6 +- .../scala/hkmc2/codegen/BlockTraverser.scala | 148 ++++++++++++++++++ .../scala/hkmc2/codegen/LambdaRewriter.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 115 ++++++-------- .../hkmc2/codegen/StackSafeTransform.scala | 10 +- 5 files changed, 202 insertions(+), 79 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index ce2f75b1d..a5d60ff42 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -242,6 +242,8 @@ class BlockTransformerShallow(subst: SymbolSubst) extends BlockTransformer(subst then b else HandleBlock(l2, res2, par2, args2, cls2, hdr2, bod, rst2) case _ => super.applyBlock(b) -// does not traverse into any other block -class BlockTransformerNoRec(subst: SymbolSubst) extends BlockTransformerShallow(subst): +// Does not traverse into sub-blocks or definitions. The purpose of this is is to only rewrite a block's data, i.e. +// paths, values, cases, etc. within a block. Can be used in tandem with `BlockTransformer` or `BlockTransformerShallow` +// to traverse sub-blocks while using this class to perform more complicated transformations on the blocks themselves. +class BlockDataTransformer(subst: SymbolSubst) extends BlockTransformerShallow(subst): override def applySubBlock(b: Block): Block = b diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala new file mode 100644 index 000000000..a8f4a58dd --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala @@ -0,0 +1,148 @@ +package hkmc2 +package codegen + +import mlscript.utils.*, shorthands.* +import hkmc2.utils.* + +import semantics.* +import os.write.over + +// These all work like BlockTransformer and its derivatives, but do not rewrite the block. See BlockTransformer.scala. +// Please use this instead of BlockTransformer for static analysis. + +class BlockTraverser(subst: SymbolSubst): + + given SymbolSubst = subst + + def applySubBlock(b: Block): Unit = applyBlock(b) + + def applyBlock(b: Block): Unit = b match + case _: End => () + case Break(lbl) => applyLocal(lbl) + case Continue(lbl) => applyLocal(lbl) + case Return(res, implct) => applyResult(res) + case Throw(exc) => applyResult(exc) + case HandleBlockReturn(res) => applyResult(res) + case Match(scrut, arms, dflt, rst) => + val scrut2 = applyPath(scrut) + arms.map: arm => + applyCase(arm._1); applySubBlock(arm._2) + dflt.map(applySubBlock) + applySubBlock(rst) + case Label(lbl, bod, rst) => applyLocal(lbl); applySubBlock(bod); applySubBlock(rst) + case Begin(sub, rst) => applySubBlock(sub); applySubBlock(rst) + case TryBlock(sub, fin, rst) => applySubBlock(sub); applySubBlock(fin); applySubBlock(rst) + case Assign(l, r, rst) => applyLocal(l); applyResult(r); applySubBlock(rst) + case b @ AssignField(l, n, r, rst) => + applyPath(l); applyResult(r); applySubBlock(rst); b.symbol.map(_.subst) + case Define(defn, rst) => applyDefn(defn); applySubBlock(rst) + case HandleBlock(l, res, par, args, cls, hdr, bod, rst) => + applyLocal(l) + applyLocal(res) + applyPath(par) + args.map(applyPath) + hdr.map(applyHandler) + applySubBlock(bod) + applySubBlock(rst) + case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => + applyPath(lhs) + applyResult(rhs) + applyPath(fld) + applySubBlock(rest) + + def applyResult(r: Result): Unit = r match + case r @ Call(fun, args) => applyPath(fun); args.map(applyArg) + case Instantiate(cls, args) =>; applyPath(cls); args.map(applyPath) + case p: Path => applyPath(p) + + def applyPath(p: Path): Unit = p match + case DynSelect(qual, fld, arrayIdx) => + applyPath(qual); applyPath(fld) + case p @ Select(qual, name) => + applyPath(qual); p.symbol.map(_.subst) + case v: Value => applyValue(v) + + def applyValue(v: Value): Unit = v match + case Value.Ref(l) => l.subst + case Value.This(sym) => sym.subst + case Value.Lit(lit) => () + case v @ Value.Lam(params, body) => applyLam(v) + case Value.Arr(elems) => elems.map(applyArg) + + def applyLocal(sym: Local): Unit = sym.subst + + def applyFunDefn(fun: FunDefn): Unit = + fun.owner.map(_.subst) + fun.sym.subst + fun.params.map(applyParamList) + applySubBlock(fun.body) + + def applyDefn(defn: Defn): Unit = defn match + case defn: FunDefn => applyFunDefn(defn) + case ValDefn(owner, k, sym, rhs) => owner.map(_.subst); sym.subst; applyPath(rhs) + case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentPath, methods, + privateFields, publicFields, preCtor, ctor) => + own.map(_.subst) + isym.subst + sym.subst + paramsOpt.map(applyParamList) + auxParams.map(applyParamList) + parentPath.map(applyPath) + methods.map(applyFunDefn) + privateFields.map(_.subst) + publicFields.map(applyTermDefinition) + applySubBlock(preCtor) + applySubBlock(ctor) + + def applyArg(arg: Arg): Unit = + applyPath(arg.value) + + def applyParamList(pl: ParamList): Unit = + pl.params.map(_.sym.subst) + pl.restParam.map(_.sym.subst) + + def applyCase(cse: Case): Unit = cse match + case Case.Lit(lit) => () + case Case.Cls(cls, path) => + cls.subst + applyPath(path) + case Case.Tup(len, inf) => () + + def applyHandler(hdr: Handler): Unit = + hdr.sym.subst + hdr.resumeSym.subst + hdr.params.map(applyParamList) + applySubBlock(hdr.body) + + def applyLam(lam: Value.Lam): Unit = + applyParamList(lam.params) + applySubBlock(lam.body) + + def applyTermDefinition(td: TermDefinition): Unit = + td.owner.map(_.subst) + td.sym.subst + td.params.map(applyParamList) + td.resSym.subst + +class BlockTraverserShallow(subst: SymbolSubst) extends BlockTraverser(subst): + override def applyLam(lam: Value.Lam) = () + override def applyFunDefn(fun: FunDefn): Unit = () + override def applyDefn(defn: Defn): Unit = defn match + case _: FunDefn | _: ClsLikeDefn => () + case _: ValDefn => super.applyDefn(defn) + + override def applyHandler(hdr: Handler): Unit = () + + override def applyBlock(b: Block): Unit = b match + case HandleBlock(l, res, par, args, cls, hdr, bod, rst) => + applyLocal(l) + applyLocal(res) + applyPath(par) + args.map(applyPath) + cls.subst + hdr.map(applyHandler) + applySubBlock(rst) + case _ => super.applyBlock(b) + +class BlockDataTraverse(subst: SymbolSubst) extends BlockTraverserShallow(subst): + override def applySubBlock(b: Block): Unit = () diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala index feff77ebd..334881099 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -11,7 +11,7 @@ object LambdaRewriter: def desugar(b: Block)(using State) = def rewriteOneBlk(b: Block) = var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil - val lambdaRewriter = new BlockTransformerNoRec(SymbolSubst()): + val lambdaRewriter = new BlockDataTransformer(SymbolSubst()): override def applyValue(v: Value): Value = v match case lam: Value.Lam => val sym = BlockMemberSymbol("lambda", Nil) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index f78d77ab9..c0703766d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -291,7 +291,7 @@ class Lifter(using State, Raise): !candVars.intersect(captureFnVars).isEmpty /** - * Gets the immutable local variables of a function that need to captured by a definition being lifted. + * 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. @@ -332,8 +332,8 @@ class Lifter(using State, Raise): case _ => () // search for modules - val walker = new BlockTransformer(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = + val walker = new BlockTraverser(SymbolSubst()): + override def applyDefn(defn: Defn): Unit = if defn === d then super.applyDefn(defn) else @@ -368,8 +368,8 @@ class Lifter(using State, Raise): ignored ++= inModTopLevel val clsSyms = clsSymToBms.values.toSet - val walker2 = new BlockTransformer(SymbolSubst()): - override def applyCase(cse: Case): Case = + val walker2 = new BlockTraverser(SymbolSubst()): + override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls, path) => clsSymToBms.get(cls) match @@ -381,57 +381,42 @@ class Lifter(using State, Raise): ignored += value case _ => () case _ => () - cse - override def applyResult(r: Result): Result = r match + override def applyResult(r: Result): Unit = r match case Call(Value.Ref(_: BlockMemberSymbol), args) => args.map(applyArg) - r case Instantiate(InstSel(_), args) => args.map(applyPath) - r case _ => super.applyResult(r) // don't search within `extends`, otherwise it'll think it's used as a first-class class - override def applyDefn(defn: Defn): Defn = defn match + override def applyDefn(defn: Defn): Unit = defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(owner, k, sym, rhs) => - val owner2 = owner.mapConserve(_.subst) - val sym2 = sym.subst - val rhs2 = applyPath(rhs) - if (owner2 is owner) && (sym2 is sym) && (rhs2 is rhs) - then defn else ValDefn(owner2, k, sym2, rhs2) + owner.mapConserve(_.subst) + sym.subst + applyPath(rhs) case ClsLikeDefn(own, isym, sym, k, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor) => - val own2 = own.mapConserve(_.subst) - val isym2 = isym.subst - val sym2 = sym.subst - val paramsOpt2 = paramsOpt.mapConserve(applyParamList) - val auxParams2 = auxParams.mapConserve(applyParamList) - val methods2 = methods.mapConserve(applyFunDefn) - val privateFields2 = privateFields.mapConserve(_.subst) - val publicFields2 = publicFields.mapConserve(applyTermDefinition) - val preCtor2 = applyBlock(preCtor) - val ctor2 = applyBlock(ctor) - if (own2 is own) && (isym2 is isym) && (sym2 is sym) && - (paramsOpt2 is paramsOpt) && - (auxParams2 is auxParams) && - (methods2 is methods) && - (privateFields2 is privateFields) && - (publicFields2 is publicFields) && - (preCtor2 is preCtor) && (ctor2 is ctor) - then defn else ClsLikeDefn(own2, isym2, sym2, k, paramsOpt2, - auxParams2, parentPath, methods2, privateFields2, publicFields2, preCtor2, ctor2) - - override def applyValue(v: Value): Value = v match + own.mapConserve(_.subst) + isym.subst + sym.subst + paramsOpt.map(applyParamList) + auxParams.map(applyParamList) + methods.map(applyFunDefn) + privateFields.map(_.subst) + publicFields.map(applyTermDefinition) + applyBlock(preCtor) + applyBlock(ctor) + + override def applyValue(v: Value): Unit = v match case RefOfBms(l) if clsSyms.contains(l) && !modOrObj(ctx.defns(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 - v case _ => super.applyValue(v) walker2.applyDefn(d) @@ -593,7 +578,7 @@ class Lifter(using State, Raise): def rewriteBms(b: Block, ctx: LifterCtx) = val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty - val walker = new BlockTransformerNoRec(SymbolSubst()): + val walker = new BlockDataTransformer(SymbolSubst()): // only scan within the block. don't traverse override def applyResult(r: Result): Result = r match @@ -1107,11 +1092,10 @@ class UsedVarAnalyzer(b: Block)(using State): nestedDeep += c.sym -> nested - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Unit = inScopeDefns += defn.sym -> Set.empty createMetadataDefn(defn, b.definedVars, Set.empty) - defn walker.applyBlock(b) DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn) @@ -1128,8 +1112,8 @@ class UsedVarAnalyzer(b: Block)(using State): case Some(value) => value case None => var accessed: AccessInfo = AccessInfo.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyBlock(b: Block): Unit = b match case Assign(lhs, rhs, rest) => accessed = accessed.addMutated(lhs) applyResult(rhs) @@ -1139,13 +1123,12 @@ class UsedVarAnalyzer(b: Block)(using State): applyBlock(rest) case _ => super.applyBlock(b) - override def applyValue(v: Value): Value = v match + override def applyValue(v: Value): Unit = v match case Value.Ref(_: BuiltinSymbol) => super.applyValue(v) case RefOfBms(l) => if !isModule(l) then accessed = accessed.addRefdDefn(l) - v case Value.Ref(l) => - accessed = accessed.addAccess(l); v + accessed = accessed.addAccess(l) case _ => super.applyValue(v) walker.applyBlock(b) @@ -1190,12 +1173,12 @@ class UsedVarAnalyzer(b: Block)(using State): var defns: List[Defn] = Nil var definedVarsDeep: Set[Local] = Set.empty - val walker = new BlockTransformer(SymbolSubst()): - override def applyFunDefn(f: FunDefn): FunDefn = + val walker = new BlockTraverser(SymbolSubst()): + override def applyFunDefn(f: FunDefn): Unit = defns +:= f; definedVarsDeep ++= definedLocals(f.sym) super.applyFunDefn(f) - override def applyDefn(defn: Defn): Defn = + override def applyDefn(defn: Defn): Unit = defn match case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) case _ => @@ -1247,10 +1230,10 @@ class UsedVarAnalyzer(b: Block)(using State): private def findAccessesTop = var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = defn match + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Unit = defn match case _: FunDefn | _: ClsLikeDefn => - accessMap ++= findAccesses(defn); defn + accessMap ++= findAccesses(defn) case _ => super.applyDefn(defn) walker.applyBlock(b) accessMap @@ -1284,8 +1267,8 @@ class UsedVarAnalyzer(b: Block)(using State): def rec(blk: Block) = go(blk, reqCapture, hasReader, hasMutator) - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyBlock(b: Block): Unit = b match case Assign(lhs, rhs, rest) => applyResult(rhs) if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs @@ -1301,7 +1284,6 @@ class UsedVarAnalyzer(b: Block)(using State): infos.map(merge) // IMPORTANT: rec all first, then merge, since each branch is mutually exclusive dfltInfo.map(merge) applyBlock(rest) - b case Label(label, 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, @@ -1312,22 +1294,18 @@ class UsedVarAnalyzer(b: Block)(using State): reqCapture ++= read.intersect(blkAccessesShallow(body, S(label)).mutated) reqCapture ++= mut.intersect(body.freeVars) applyBlock(rest) - b case Begin(sub, rest) => rec(sub) |> merge applyBlock(rest) - b case TryBlock(sub, finallyDo, rest) => // sub and finallyDo could be executed sequentially, so we must merge rec(sub) |> merge rec(finallyDo) |> merge applyBlock(rest) - b case Return(res, false) => applyResult(res) hasReader = Set.empty hasMutator = Set.empty - b case _ => super.applyBlock(b) def handleCalledBms(called: BlockMemberSymbol): Unit = defnSyms.get(called) match @@ -1365,18 +1343,16 @@ class UsedVarAnalyzer(b: Block)(using State): reqCapture += l hasMutator += l - override def applyResult(r: Result): Result = r match + override def applyResult(r: Result): Unit = r match case Call(RefOfBms(l), args) => args.map(super.applyArg(_)) handleCalledBms(l) - r case Instantiate(InstSel(l), args) => args.map(super.applyPath(_)) handleCalledBms(l) - r case _ => super.applyResult(r) - override def applyPath(p: Path): Path = p match + override def applyPath(p: Path): Unit = p match case RefOfBms(l) => defnSyms.get(l) match case None => super.applyPath(p) @@ -1406,14 +1382,12 @@ class UsedVarAnalyzer(b: Block)(using State): do reqCapture += l hasMutator += l - - p + case Value.Ref(l) => if hasMutator.contains(l) then reqCapture += (l) - p case _ => super.applyPath(p) - - override def applyDefn(defn: Defn): Defn = defn match + + override def applyDefn(defn: Defn): Unit = defn match case c: ClsLikeDefn if modOrObj(c) => handleCalledBms(c.sym) super.applyDefn(defn) @@ -1460,10 +1434,9 @@ class UsedVarAnalyzer(b: Block)(using State): */ def findUsedLocals: Lifter.UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyDefn(defn: Defn): Unit = usedMap ++= findUsedLocalsDefn(defn) - defn walker.applyBlock(b) Lifter.UsedLocalsMap(usedMap) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index 3530cd233..7ece0fe30 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -135,11 +135,11 @@ class StackSafeTransform(depthLimit: Int)(using State): def isTrivial(b: Block): Boolean = var trivial = true - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyResult(r: Result): Result = r match - case Call(Value.Ref(_: BuiltinSymbol), _) => r - case _: Call | _: Instantiate => trivial = false; r - case _ => r + val walker = new BlockTraverserShallow(SymbolSubst()): + override def applyResult(r: Result): Unit = r match + case Call(Value.Ref(_: BuiltinSymbol), _) => () + case _: Call | _: Instantiate => trivial = false + case _ => () walker.applyBlock(b) trivial From 64b037a12867fb880f6bbae39939dcfe412a895a Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sun, 23 Feb 2025 17:08:04 +0800 Subject: [PATCH 105/127] update usages of BlockTraverser --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 38 +++++++++---------- .../hkmc2/codegen/StackSafeTransform.scala | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index c0703766d..78302121e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -332,7 +332,8 @@ class Lifter(using State, Raise): case _ => () // search for modules - val walker = new BlockTraverser(SymbolSubst()): + new BlockTraverser(SymbolSubst()): + applyDefn(d) override def applyDefn(defn: Defn): Unit = if defn === d then super.applyDefn(defn) @@ -352,7 +353,6 @@ class Lifter(using State, Raise): objects +:= c case _ => () super.applyDefn(defn) - walker.applyDefn(d) // search for defns nested within a top-level module, which are unnecessary to lift def inModuleDefns(d: Defn): Set[BlockMemberSymbol] = @@ -368,7 +368,8 @@ class Lifter(using State, Raise): ignored ++= inModTopLevel val clsSyms = clsSymToBms.values.toSet - val walker2 = new BlockTraverser(SymbolSubst()): + new BlockTraverser(SymbolSubst()): + applyDefn(d) override def applyCase(cse: Case): Unit = cse match case Case.Cls(cls, path) => @@ -418,7 +419,6 @@ class Lifter(using State, Raise): )) ignored += l case _ => super.applyValue(v) - walker2.applyDefn(d) (ignored, modules, objects) @@ -1092,11 +1092,11 @@ class UsedVarAnalyzer(b: Block)(using State): nestedDeep += c.sym -> nested - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) override def applyDefn(defn: Defn): Unit = inScopeDefns += defn.sym -> Set.empty createMetadataDefn(defn, b.definedVars, Set.empty) - walker.applyBlock(b) DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn) val DefnMetadata(definedLocals, defnsMap, existingVars, @@ -1112,7 +1112,9 @@ class UsedVarAnalyzer(b: Block)(using State): case Some(value) => value case None => var accessed: AccessInfo = AccessInfo.empty - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) + override def applyBlock(b: Block): Unit = b match case Assign(lhs, rhs, rest) => accessed = accessed.addMutated(lhs) @@ -1130,8 +1132,6 @@ class UsedVarAnalyzer(b: Block)(using State): case Value.Ref(l) => accessed = accessed.addAccess(l) case _ => super.applyValue(v) - - walker.applyBlock(b) cacheId match case None => () @@ -1173,7 +1173,9 @@ class UsedVarAnalyzer(b: Block)(using State): var defns: List[Defn] = Nil var definedVarsDeep: Set[Local] = Set.empty - val walker = new BlockTraverser(SymbolSubst()): + new BlockTraverser(SymbolSubst()): + applyDefn(d) + override def applyFunDefn(f: FunDefn): Unit = defns +:= f; definedVarsDeep ++= definedLocals(f.sym) super.applyFunDefn(f) @@ -1183,8 +1185,6 @@ class UsedVarAnalyzer(b: Block)(using State): case c: ClsLikeDefn => defns +:= c; definedVarsDeep ++= definedLocals(c.sym) case _ => super.applyDefn(defn) - - walker.applyDefn(d) val defnSyms = defns.map(_.sym).toSet val accessInfo = defns.map: d => @@ -1230,12 +1230,13 @@ class UsedVarAnalyzer(b: Block)(using State): private def findAccessesTop = var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) override def applyDefn(defn: Defn): Unit = defn match case _: FunDefn | _: ClsLikeDefn => accessMap ++= findAccesses(defn) case _ => super.applyDefn(defn) - walker.applyBlock(b) + accessMap val accessMap = findAccessesTop @@ -1267,7 +1268,8 @@ class UsedVarAnalyzer(b: Block)(using State): def rec(blk: Block) = go(blk, reqCapture, hasReader, hasMutator) - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) override def applyBlock(b: Block): Unit = b match case Assign(lhs, rhs, rest) => applyResult(rhs) @@ -1393,8 +1395,6 @@ class UsedVarAnalyzer(b: Block)(using State): super.applyDefn(defn) case _ => super.applyDefn(defn) - walker.applyBlock(b) - CaptureInfo(reqCapture, hasReader, hasMutator) val reqCapture = go(f.body, Set.empty, Set.empty, Set.empty).reqCapture @@ -1434,9 +1434,9 @@ class UsedVarAnalyzer(b: Block)(using State): */ def findUsedLocals: Lifter.UsedLocalsMap = var usedMap: Map[BlockMemberSymbol, FreeVars] = Map.empty - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) override def applyDefn(defn: Defn): Unit = usedMap ++= findUsedLocalsDefn(defn) - walker.applyBlock(b) Lifter.UsedLocalsMap(usedMap) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index 7ece0fe30..becde0ddb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -135,12 +135,12 @@ class StackSafeTransform(depthLimit: Int)(using State): def isTrivial(b: Block): Boolean = var trivial = true - val walker = new BlockTraverserShallow(SymbolSubst()): + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) override def applyResult(r: Result): Unit = r match case Call(Value.Ref(_: BuiltinSymbol), _) => () case _: Call | _: Instantiate => trivial = false case _ => () - walker.applyBlock(b) trivial def rewriteCls(defn: ClsLikeDefn, isTopLevel: Bool): ClsLikeDefn = From 90bc4960b1a55a072adf964edce18c468d9be4ea Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:09:47 +0800 Subject: [PATCH 106/127] refactor refRewriter into BlockRewriter --- .../src/main/scala/hkmc2/codegen/Block.scala | 1 + .../src/main/scala/hkmc2/codegen/Lifter.scala | 217 +++++++++++++----- .../src/test/mlscript/lifter/DefnsInClass.mls | 21 +- 3 files changed, 168 insertions(+), 71 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 96826da79..f9c3b530a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -433,6 +433,7 @@ case class Instantiate(cls: Path, args: Ls[Path]) extends Result sealed abstract class Path extends Result: def selN(id: Tree.Ident): Path = Select(this, id)(N) + def sel(id: Tree.Ident, sym: FieldSymbol): Path = Select(this, id)(S(sym)) def selSN(id: Str): Path = selN(new Tree.Ident(id)) def asArg = Arg(false, this) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 78302121e..df78c2a7b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -391,7 +391,6 @@ class Lifter(using State, Raise): case _ => super.applyResult(r) - // don't search within `extends`, otherwise it'll think it's used as a first-class class override def applyDefn(defn: Defn): Unit = defn match case defn: FunDefn => applyFunDefn(defn) case ValDefn(owner, k, sym, rhs) => @@ -403,6 +402,23 @@ class Lifter(using State, Raise): own.mapConserve(_.subst) isym.subst sym.subst + // `parentPath` is currently not checked, as checking it as shown below breaks lowering continuation classes + // and other handler-related classes. + /* + // Check if `extends` is a complex expression, i.e. not just extending a class + parentPath match + case None => () + case Some(Select(RefOfBms(s), Tree.Ident("class"))) if !ignored.contains(s) => () + case Some(RefOfBms(s)) if !ignored.contains(s) => () + case _ if !ignored.contains(defn.sym) => + println(parentPath) + raise(WarningReport( + msg"Cannot yet lift class/module `${sym.nme}` as it extends a first-class class or an expression." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) + ignored += defn.sym + case _ => () + */ paramsOpt.map(applyParamList) auxParams.map(applyParamList) methods.map(applyFunDefn) @@ -569,69 +585,143 @@ class Lifter(using State, Raise): case Some(value) => Value.Ref(value) case None => super.applyPath(p) case _ => super.applyPath(p) - - def rewriteBlk(b: Block, ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx): Block = - // replaces references to BlockMemberSymbols as needed with fresh variables, and - // returns the mapping from the symbol to the required variable. When possible, - // it also directly rewrites Results. - - def rewriteBms(b: Block, ctx: LifterCtx) = - val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty - - val walker = new BlockDataTransformer(SymbolSubst()): - // only scan within the block. don't traverse - override def applyResult(r: Result): Result = r match - // if possible, directly rewrite the call using the efficient version - case c @ Call(RefOfBms(l), args) => ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModOrObj(l) => - val extraArgs = getCallArgs(l, ctx) - val newArgs = args.map(applyArg(_)) - Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) - case _ => super.applyResult(r) - case c @ Instantiate(InstSel(l), args) => - ctx.bmsReqdInfo.get(l) match - case Some(info) if !ctx.isModOrObj(l) => - val extraArgs = getCallArgs(l, ctx) - val newArgs = args.map(applyPath(_)).map(_.asArg) - Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) - case _ => super.applyResult(r) - // if possible, directly create the bms and replace the result with it - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => - createCall(l, ctx) + // replaces references to BlockMemberSymbols as needed with fresh variables, and + // returns the mapping from the symbol to the required variable. When possible, + // it also directly rewrites Results. + def rewriteBms(b: Block, ctx: LifterCtx) = + val syms: LinkedHashMap[BlockMemberSymbol, Local] = LinkedHashMap.empty + + val walker = new BlockDataTransformer(SymbolSubst()): + // only scan within the block. don't traverse + + override def applyResult(r: Result): Result = r match + // if possible, directly rewrite the call using the efficient version + case c @ Call(RefOfBms(l), args) => ctx.bmsReqdInfo.get(l) match + case Some(info) if !ctx.isModOrObj(l) => + val extraArgs = getCallArgs(l, ctx) + val newArgs = args.map(applyArg(_)) + Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(c.isMlsFun, false) + case _ => super.applyResult(r) + case c @ Instantiate(InstSel(l), args) => + ctx.bmsReqdInfo.get(l) match + case Some(info) if !ctx.isModOrObj(l) => + val extraArgs = getCallArgs(l, ctx) + val newArgs = args.map(applyPath(_)).map(_.asArg) + Call(info.singleCallBms.asPath, extraArgs ++ newArgs)(true, false) case _ => super.applyResult(r) + // if possible, directly create the bms and replace the result with it + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => + createCall(l, ctx) + case _ => super.applyResult(r) + + // otherwise, there's no choice but to create the call earlier + override def applyPath(p: Path): Path = p match + case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => + val newSym = syms.get(l) match + case None => + val newSym = FlowSymbol(l.nme + "$this") + syms.addOne(l -> newSym) + newSym + case Some(value) => value + Value.Ref(newSym) + case _ => super.applyPath(p) + (walker.applyBlock(b), syms.toList) + end rewriteBms + + class BlockRewriter(ctorCls: Opt[ClsLikeDefn], ctx: LifterCtx) extends BlockTransformerShallow(SymbolSubst()): + def belongsToCtor(l: Symbol) = + ctorCls match + case None => false + case Some(value) => + value.isym === l - // otherwise, there's no choice but to create the call earlier - override def applyPath(p: Path): Path = p match - case RefOfBms(l) if ctx.bmsReqdInfo.contains(l) && !ctx.isModOrObj(l) => - val newSym = syms.get(l) match - case None => - val newSym = FlowSymbol(l.nme + "$this") - syms.addOne(l -> newSym) - newSym - case Some(value) => value - Value.Ref(newSym) - case _ => super.applyPath(p) - (walker.applyBlock(b), syms.toList) - end rewriteBms - + override def applyBlock(b: Block): Block = + val (rewritten, syms) = rewriteBms(b, ctx) + val pre = syms.foldLeft(blockBuilder): + case (blk, (bms, local)) => + val initial = blk.assign(local, createCall(bms, ctx)) + ctx.defns(bms) match + case c: ClsLikeDefn => initial.assignFieldN(local.asPath, Tree.Ident("class"), bms.asPath) + case _ => initial + + val remaining = rewritten match + case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match + case Some(value) if !belongsToCtor(lhs) => + Assign(value, applyResult(rhs), applyBlock(rest)) + case _ => super.applyBlock(rewritten) + + case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(t.owner.get) => + AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) + case _ => super.applyBlock(rewritten) + + case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match + case Some(captureSym) => + AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) + case None => ctx.getLocalPath(lhs) match + case None => super.applyBlock(rewritten) + case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) + + case Define(d: Defn, rest: Block) => ctx.modLocals.get(d.sym) match + case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match + case Some(_) => + blockBuilder + .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) + .rest(applyBlock(rest)) + case None => + blockBuilder + .assign(sym, Call(d.sym.asPath, Nil)(true, false)) + .rest(applyBlock(rest)) + 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) - // rewrites references to variables and defns - val transformer1 = refRewriter(ctx, ctorCls) - - // rewrites references to block member symbols - val transformer2 = new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = - val (rewriten, syms) = rewriteBms(b, ctx) - val pre = syms.foldLeft(blockBuilder): - case (blk, (bms, local)) => - val initial = blk.assign(local, createCall(bms, ctx)) - ctx.defns(bms) match - case c: ClsLikeDefn => initial.assignFieldN(local.asPath, Tree.Ident("class"), bms.asPath) - case _ => initial - pre.rest(super.applyBlock(rewriten)) - - b |> transformer1.applyBlock |> transformer2.applyBlock + override def applyPath(p: Path): Path = + p match + // These two cases rewrites `this.whatever` when referencing an outer class's fields. + case Value.Ref(l: InnerSymbol) => + ctx.getIsymPath(l) match + case Some(value) if !belongsToCtor(l) => Value.Ref(value) + case _ => super.applyPath(p) + case Value.Ref(t: TermSymbol) if t.owner.isDefined => + ctx.getIsymPath(t.owner.get) match + case Some(value) if !belongsToCtor(t.owner.get) => Select(value.asPath, t.id)(N) + case _ => super.applyPath(p) + + // Rewrites this.className.class to reference the top-level definition + case s @ Select(RefOfBms(l), Tree.Ident("class")) if !ctx.ignored(l) && ctx.isRelevant(l) => + // this class will be lifted, rewrite the ref to strip it of `Select` + Select(Value.Ref(l), Tree.Ident("class"))(s.symbol) + + // 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(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) + case _ => super.applyPath(p) + + // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol + // reference is passed as function parameters). + case RefOfBms(l) if ctx.ignored(l) && ctx.isRelevant(l) => ctx.getIgnoredBmsPath(l) match + case Some(value) => value + case None => super.applyPath(p) + + // 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) => + Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) + case None => ctx.getLocalPath(l) match + case Some(value) => Value.Ref(value) + case None => super.applyPath(p) + case _ => super.applyPath(p) def getCallArgs(sym: BlockMemberSymbol, ctx: LifterCtx) = val info = ctx.getBmsReqdInfo(sym).get @@ -850,8 +940,9 @@ class Lifter(using State, Raise): .toMap val replacedDefnsCtx = newCtx.addreplacedDefns(ctorIgnoredRewrite) - val newPreCtor = rewriteBlk(preCtor, S(c), replacedDefnsCtx) - val newCtor = rewriteBlk(ctor, S(c), replacedDefnsCtx) + val rewriter = BlockRewriter(S(c), replacedDefnsCtx) + val newPreCtor = rewriter.applyBlock(preCtor) + val newCtor = rewriter.applyBlock(ctor) val fLifted = c.methods.map(liftDefnsInFn(_, newCtx)) @@ -924,7 +1015,7 @@ class Lifter(using State, Raise): lifted.liftedDefn.sym -> lifted.liftedDefn .toMap - val transformed = rewriteBlk(blk, N, captureCtx.addreplacedDefns(ignoredRewrite)) + val transformed = BlockRewriter(N, captureCtx.addreplacedDefns(ignoredRewrite)).applyBlock(blk) if thisVars.reqCapture.size == 0 then Lifted(FunDefn(f.owner, f.sym, f.params, transformed), newDefns) diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index d84d21550..20cf70157 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -118,20 +118,24 @@ A(1).getA() //│ ═══[RUNTIME ERROR] TypeError: this.B is not a function //│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' -:sjs +:ssjs class A with val x = fun g() = 2 g (new A).x() -//│ JS (unsanitized): -//│ let g, A5, tmp2, g$; -//│ g$ = function g$(A$instance) { +//│ JS: +//│ g$ = function g$(...args) { +//│ globalThis.Predef.checkArgs("g$", 1, true, args.length); +//│ let A$instance = args[0]; //│ return 2 //│ }; -//│ g = function g(A$instance) { -//│ return () => { -//│ return g$(A$instance) +//│ g = function g(...args) { +//│ globalThis.Predef.checkArgs("g", 1, true, args.length); +//│ let A$instance = args[0]; +//│ return (...args1) => { +//│ globalThis.Predef.checkArgs("", 0, true, args1.length); +//│ return runtime.checkCall(g$(A$instance)) //│ } //│ }; //│ A5 = class A4 { @@ -143,5 +147,6 @@ class A with //│ toString() { return "A"; } //│ }; //│ tmp2 = new A5(); -//│ runtime.safeCall(tmp2.x()) +//│ block$res5 = runtime.safeCall(tmp2.x()); +//│ undefined //│ = 2 From f1d35172e8ee88886ca631faef1bfc7553211c1d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:10:57 +0800 Subject: [PATCH 107/127] remove refRewriter --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 85 ------------------- 1 file changed, 85 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index df78c2a7b..c275c843f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -501,91 +501,6 @@ class Lifter(using State, Raise): defns.flatMap(f => createLiftInfoCont(f, S(c), newCtx)).toMap ++ c.methods.flatMap(f => createLiftInfoFn(f, newCtx)) - def refRewriter(ctx: LifterCtx, ctorCls: Opt[ClsLikeDefn]) = - def belongsToCtor(l: Symbol) = - ctorCls match - case None => false - case Some(value) => - value.isym === l - - new BlockTransformerShallow(SymbolSubst()): - override def applyBlock(b: Block): Block = b match - case Assign(lhs: InnerSymbol, rhs, rest) => ctx.getIsymPath(lhs) match - case Some(value) if !belongsToCtor(lhs) => - Assign(value, applyResult(rhs), applyBlock(rest)) - case _ => super.applyBlock(b) - - case Assign(t: TermSymbol, rhs, rest) if t.owner.isDefined => - ctx.getIsymPath(t.owner.get) match - case Some(value) if !belongsToCtor(t.owner.get) => - AssignField(value.asPath, t.id, applyResult(rhs), applyBlock(rest))(N) - case _ => super.applyBlock(b) - - case Assign(lhs, rhs, rest) => ctx.getLocalCaptureSym(lhs) match - case Some(captureSym) => - AssignField(ctx.getLocalClosPath(lhs).get, captureSym.id, applyResult(rhs), applyBlock(rest))(N) - case None => ctx.getLocalPath(lhs) match - case None => super.applyBlock(b) - case Some(value) => Assign(value, applyResult(rhs), applyBlock(rest)) - - case Define(d: Defn, rest: Block) => ctx.modLocals.get(d.sym) match - case Some(sym) if !ctx.ignored(d.sym) => ctx.getBmsReqdInfo(d.sym) match - case Some(_) => - blockBuilder - .assign(sym, Call(d.sym.asPath, getCallArgs(d.sym, ctx))(true, false)) - .rest(applyBlock(rest)) - case None => - blockBuilder - .assign(sym, Call(d.sym.asPath, Nil)(true, false)) - .rest(applyBlock(rest)) - case _ => ctx.replacedDefns.get(d.sym) match - case Some(value) => Define(value, applyBlock(rest)) - case None => super.applyBlock(b) - - - case _ => super.applyBlock(b) - - override def applyPath(p: Path): Path = - p match - // These two cases rewrites `this.whatever` when referencing an outer class's fields. - case Value.Ref(l: InnerSymbol) => - ctx.getIsymPath(l) match - case Some(value) if !belongsToCtor(l) => Value.Ref(value) - case _ => super.applyPath(p) - case Value.Ref(t: TermSymbol) if t.owner.isDefined => - ctx.getIsymPath(t.owner.get) match - case Some(value) if !belongsToCtor(t.owner.get) => Select(value.asPath, t.id)(N) - case _ => super.applyPath(p) - - // Rewrites this.className.class to reference the top-level definition - case s @ Select(RefOfBms(l), Tree.Ident("class")) if !ctx.ignored(l) && ctx.isRelevant(l) => - // this class will be lifted, rewrite the ref to strip it of `Select` - Select(Value.Ref(l), Tree.Ident("class"))(s.symbol) - - // 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(value: MemberSymbol[?]) => Select(qual, Tree.Ident(value.nme))(S(value)) - case _ => super.applyPath(p) - - // This is to rewrite references to classes that are not lifted (when their BlockMemberSymbol - // reference is passed as function parameters). - case RefOfBms(l) if ctx.ignored(l) && ctx.isRelevant(l) => ctx.getIgnoredBmsPath(l) match - case Some(value) => value - case None => super.applyPath(p) - - // 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) => - Select(ctx.getLocalClosPath(l).get, captureSym.id)(N) - case None => ctx.getLocalPath(l) match - case Some(value) => Value.Ref(value) - case None => super.applyPath(p) - case _ => super.applyPath(p) - // replaces references to BlockMemberSymbols as needed with fresh variables, and // returns the mapping from the symbol to the required variable. When possible, // it also directly rewrites Results. From 1d7ee18e84d0991bf781ec6bd058e41cb87744a4 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:21:27 +0800 Subject: [PATCH 108/127] add spread arg test --- .../src/test/mlscript/backlog/Lifter.mls | 19 +++++++++++++++++++ .../src/test/mlscript/lifter/FunInFun.mls | 14 ++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 3da716db8..14e67e152 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -2,6 +2,25 @@ :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 +//│ ═══[RUNTIME ERROR] Expected: '[1]', got: '[]' +//│ = [] + +// The following are problems with lifting classes inside other definitions. + // We use the optional `symbol` parameter of `Select` to detect references to the // BlockMemberSymbol. But when this symbol is not present, there is no way to properly // detect it. What do to? diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 96c5a850e..3593b48ab 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -627,3 +627,17 @@ fun f() = set n = n + 1 g() f() + +:fixme +:expect [1] +:lift +fun f(x) = + fun g(...rest) = + print(x) + rest + let a = g + a(1) +f(2) +//│ > 2 +//│ ═══[RUNTIME ERROR] Expected: '[1]', got: '[]' +//│ = [] From 0f8e84e17257606ee694a3d0140f3f10d838803c Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:38:07 +0800 Subject: [PATCH 109/127] merge walkers --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index c275c843f..f3b65e79e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -961,40 +961,27 @@ class Lifter(using State, Raise): .withAccesses(analyzer.accessMap) .withInScopes(analyzer.inScopeDefns) - var unliftable: Set[BlockMemberSymbol] = Set.empty - var modules: List[ClsLikeDefn] = List.empty - var objects: List[ClsLikeDefn] = List.empty - - - val walker = new BlockTransformerShallow(SymbolSubst()): - override def applyDefn(defn: Defn): Defn = - val (unl, mod, obj) = createMetadata(defn, ctx) - unliftable ++= unl - modules ++= mod - objects ++= obj - defn - - walker.applyBlock(b) - - val modLocals = (modules ++ objects).map: c => - analyzer.nestedIn.get(c.sym) match - case Some(bms) => - val nestedIn = analyzer.defnsMap(bms) - nestedIn match - 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 - - val ctxx = ctx - .addIgnored(unliftable) - .withModLocals(modLocals) - val walker1 = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match case Define(d, rest) => + val (unliftable, modules, objects) = createMetadata(d, ctx) + + val modLocals = (modules ++ objects).map: c => + analyzer.nestedIn.get(c.sym) match + case Some(bms) => + val nestedIn = analyzer.defnsMap(bms) + nestedIn match + 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 + + val ctxx = ctx + .addIgnored(unliftable) + .withModLocals(modLocals) + val Lifted(lifted, extra) = d match case f: FunDefn => val ctxxx = ctxx.withDefnsCur(analyzer.nestedDeep(d.sym)) From 1584cb7efd2eb4209c8dec6beeccbb234e3e69fa Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:41:29 +0800 Subject: [PATCH 110/127] Move UsedVarAnalyzer to new file --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 440 ----------------- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 449 ++++++++++++++++++ 2 files changed, 449 insertions(+), 440 deletions(-) create mode 100644 hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index f3b65e79e..a59e806a7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -993,443 +993,3 @@ class Lifter(using State, Raise): (lifted :: extra).foldLeft(applyBlock(rest))((acc, defn) => Define(defn, acc)) case _ => super.applyBlock(b) walker1.applyBlock(blk) -/** - * 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 - * allocation (during class lifting) despite being mutable. - * - * Assumes the input trees have no lambdas. - */ -class UsedVarAnalyzer(b: Block)(using State): - import Lifter.* - - // 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 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 - ) - 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 - - 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.floatOutDefns()._2 - - 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.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2) - - 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 - - new BlockTraverserShallow(SymbolSubst()): - applyBlock(b) - override def applyDefn(defn: Defn): Unit = - inScopeDefns += defn.sym -> Set.empty - createMetadataDefn(defn, b.definedVars, Set.empty) - DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn) - - val DefnMetadata(definedLocals, defnsMap, existingVars, - inScopeDefns, nestedDefns, nestedDeep, nestedIn) = createMetadata - - def isModule(s: BlockMemberSymbol) = defnsMap.get(s) match - case S(c: ClsLikeDefn) => c.k is syntax.Mod - case _ => false - - 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(SymbolSubst()): - 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, 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) => - if !isModule(l) then 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 - - /** - * 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. - * @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: List[Defn] = Nil - var definedVarsDeep: Set[Local] = Set.empty - - new BlockTraverser(SymbolSubst()): - 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) - - val defnSyms = defns.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 accessInfoMap = accessInfo.toMap - - val edges = - for - (sym, AccessInfo(_, _, refd)) <- accessInfo - r <- refd - if defnSyms.contains(r) - yield sym -> 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) - - // 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).intersectLocals(existingVars(sym))) - - private def findAccessesTop = - var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty - new BlockTraverserShallow(SymbolSubst()): - applyBlock(b) - override def applyDefn(defn: Defn): Unit = defn match - case _: FunDefn | _: ClsLikeDefn => - accessMap ++= findAccesses(defn) - case _ => super.applyDefn(defn) - - 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.floatOutDefns() - val defnSyms = defns.collect: - case f: FunDefn => f.sym -> f - case c: ClsLikeDefn => c.sym -> c - .toMap - - val thisVars = definedLocals(f.sym) - - 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 = - var reqCapture = reqCapture_ - var hasReader = hasReader_ - var hasMutator = hasMutator_ - - inline def merge(c: CaptureInfo) = - reqCapture ++= c.reqCapture - hasReader ++= c.hasReader - hasMutator ++= c.hasMutator - - def rec(blk: Block) = - go(blk, reqCapture, hasReader, hasMutator) - - new BlockTraverserShallow(SymbolSubst()): - applyBlock(b) - override def applyBlock(b: Block): Unit = b match - case Assign(lhs, rhs, rest) => - applyResult(rhs) - if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs - applyBlock(rest) - - case Match(scrut, arms, dflt, rest) => - applyPath(scrut) - val infos = arms.map: - case (_, arm) => rec(arm) - 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, 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) - applyBlock(rest) - case Begin(sub, rest) => - rec(sub) |> merge - applyBlock(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) - case Return(res, false) => - applyResult(res) - hasReader = Set.empty - hasMutator = Set.empty - case _ => super.applyBlock(b) - - def handleCalledBms(called: BlockMemberSymbol): Unit = defnSyms.get(called) match - case None => () - case Some(defn) => - // special case continuation classes - defn match - case c: ClsLikeDefn => c.parentPath match - case S(path) if Lifter.isContClassPth(path) => return - // treat the continuation class as if it does not exist - case _ => () - case _ => () - - - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) - val muts = muted.intersect(thisVars) - val reads = defn.freeVars.intersect(thisVars) -- muts - // 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 - reqCapture += l - hasMutator += l - for l <- reads do - if hasMutator.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 - // many mutators could be created from it - for - sym <- refd - l <- accessMap(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(InstSel(l), args) => - args.map(super.applyPath(_)) - handleCalledBms(l) - case _ => super.applyResult(r) - - override def applyPath(p: Path): Unit = p match - case RefOfBms(l) => - defnSyms.get(l) match - case None => super.applyPath(p) - case Some(defn) => - val isMod = defn match - case c: ClsLikeDefn => modOrObj(c) - case _ => false - if isMod then super.applyPath(p) - else - val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) - val muts = muted.intersect(thisVars) - val reads = defn.freeVars.intersect(thisVars) -- muts - // this is a naked reference, we assume things it mutates always needs a capture - for l <- muts do - reqCapture += l - hasMutator += l - for l <- reads do - if hasMutator.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 - // many mutators could be created from it - for - sym <- refd - l <- accessMap(sym).mutated - do - reqCapture += l - hasMutator += l - - case Value.Ref(l) => - if hasMutator.contains(l) then reqCapture += (l) - case _ => super.applyPath(p) - - override def applyDefn(defn: Defn): Unit = defn match - case c: ClsLikeDefn if modOrObj(c) => - handleCalledBms(c.sym) - 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 - - private def findUsedLocalsDefn(d: Defn) = - d match - case f: FunDefn => - findUsedLocalsFn(f) - case c: ClsLikeDefn => - findUsedLocalsCls(c) - case d => Map.empty - - private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = - nestedDefns(c.sym).foldLeft(Map.empty): - case (acc, d) => acc ++ findUsedLocalsDefn(d) - - /** - * 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(SymbolSubst()): - applyBlock(b) - override def applyDefn(defn: Defn): Unit = - usedMap ++= findUsedLocalsDefn(defn) - - Lifter.UsedLocalsMap(usedMap) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala new file mode 100644 index 000000000..3c8532c02 --- /dev/null +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -0,0 +1,449 @@ +package hkmc2 + +import mlscript.utils.*, shorthands.* +import utils.* + +import hkmc2.codegen.* +import hkmc2.semantics.* +import hkmc2.Message.* +import hkmc2.semantics.Elaborator.State + +import scala.collection.mutable.Map as MutMap + +/** + * 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 + * allocation (during class lifting) despite being mutable. + * + * 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 + ) + 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 + + 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.floatOutDefns()._2 + + 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.floatOutDefns()._2 ++ c.ctor.floatOutDefns()._2) + + 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 + + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) + override def applyDefn(defn: Defn): Unit = + inScopeDefns += defn.sym -> Set.empty + createMetadataDefn(defn, b.definedVars, Set.empty) + DefnMetadata(definedLocals, defnsMap, existingVars, inScopeDefns, nestedDefns, nestedDeep, nestedIn) + + val DefnMetadata(definedLocals, defnsMap, existingVars, + inScopeDefns, nestedDefns, nestedDeep, nestedIn) = createMetadata + + def isModule(s: BlockMemberSymbol) = defnsMap.get(s) match + case S(c: ClsLikeDefn) => c.k is syntax.Mod + case _ => false + + 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(SymbolSubst()): + 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, 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) => + if !isModule(l) then 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 + + /** + * 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. + * @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: List[Defn] = Nil + var definedVarsDeep: Set[Local] = Set.empty + + new BlockTraverser(SymbolSubst()): + 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) + + val defnSyms = defns.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 accessInfoMap = accessInfo.toMap + + val edges = + for + (sym, AccessInfo(_, _, refd)) <- accessInfo + r <- refd + if defnSyms.contains(r) + yield sym -> 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) + + // 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).intersectLocals(existingVars(sym))) + + private def findAccessesTop = + var accessMap: Map[BlockMemberSymbol, AccessInfo] = Map.empty + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) + override def applyDefn(defn: Defn): Unit = defn match + case _: FunDefn | _: ClsLikeDefn => + accessMap ++= findAccesses(defn) + case _ => super.applyDefn(defn) + + 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.floatOutDefns() + val defnSyms = defns.collect: + case f: FunDefn => f.sym -> f + case c: ClsLikeDefn => c.sym -> c + .toMap + + val thisVars = definedLocals(f.sym) + + 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 = + var reqCapture = reqCapture_ + var hasReader = hasReader_ + var hasMutator = hasMutator_ + + inline def merge(c: CaptureInfo) = + reqCapture ++= c.reqCapture + hasReader ++= c.hasReader + hasMutator ++= c.hasMutator + + def rec(blk: Block) = + go(blk, reqCapture, hasReader, hasMutator) + + new BlockTraverserShallow(SymbolSubst()): + applyBlock(b) + override def applyBlock(b: Block): Unit = b match + case Assign(lhs, rhs, rest) => + applyResult(rhs) + if hasReader.contains(lhs) || hasMutator.contains(lhs) then reqCapture += lhs + applyBlock(rest) + + case Match(scrut, arms, dflt, rest) => + applyPath(scrut) + val infos = arms.map: + case (_, arm) => rec(arm) + 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, 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) + applyBlock(rest) + case Begin(sub, rest) => + rec(sub) |> merge + applyBlock(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) + case Return(res, false) => + applyResult(res) + hasReader = Set.empty + hasMutator = Set.empty + case _ => super.applyBlock(b) + + def handleCalledBms(called: BlockMemberSymbol): Unit = defnSyms.get(called) match + case None => () + case Some(defn) => + // special case continuation classes + defn match + case c: ClsLikeDefn => c.parentPath match + case S(path) if Lifter.isContClassPth(path) => return + // treat the continuation class as if it does not exist + case _ => () + case _ => () + + + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val muts = muted.intersect(thisVars) + val reads = defn.freeVars.intersect(thisVars) -- muts + // 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 + reqCapture += l + hasMutator += l + for l <- reads do + if hasMutator.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 + // many mutators could be created from it + for + sym <- refd + l <- accessMap(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(InstSel(l), args) => + args.map(super.applyPath(_)) + handleCalledBms(l) + case _ => super.applyResult(r) + + override def applyPath(p: Path): Unit = p match + case RefOfBms(l) => + defnSyms.get(l) match + case None => super.applyPath(p) + case Some(defn) => + val isMod = defn match + case c: ClsLikeDefn => modOrObj(c) + case _ => false + if isMod then super.applyPath(p) + else + val AccessInfo(accessed, muted, refd) = accessMap(defn.sym) + val muts = muted.intersect(thisVars) + val reads = defn.freeVars.intersect(thisVars) -- muts + // this is a naked reference, we assume things it mutates always needs a capture + for l <- muts do + reqCapture += l + hasMutator += l + for l <- reads do + if hasMutator.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 + // many mutators could be created from it + for + sym <- refd + l <- accessMap(sym).mutated + do + reqCapture += l + hasMutator += l + + case Value.Ref(l) => + if hasMutator.contains(l) then reqCapture += (l) + case _ => super.applyPath(p) + + override def applyDefn(defn: Defn): Unit = defn match + case c: ClsLikeDefn if modOrObj(c) => + handleCalledBms(c.sym) + 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 + + private def findUsedLocalsDefn(d: Defn) = + d match + case f: FunDefn => + findUsedLocalsFn(f) + case c: ClsLikeDefn => + findUsedLocalsCls(c) + case d => Map.empty + + private def findUsedLocalsCls(c: ClsLikeDefn): Map[BlockMemberSymbol, FreeVars] = + nestedDefns(c.sym).foldLeft(Map.empty): + case (acc, d) => acc ++ findUsedLocalsDefn(d) + + /** + * 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(SymbolSubst()): + applyBlock(b) + override def applyDefn(defn: Defn): Unit = + usedMap ++= findUsedLocalsDefn(defn) + + Lifter.UsedLocalsMap(usedMap) From 0d1b73b0a3ae0ef17e97ffb8c7f150ce98097083 Mon Sep 17 00:00:00 2001 From: Mark Ng <55091936+CAG2Mark@users.noreply.github.com> Date: Tue, 25 Feb 2025 09:46:32 +0000 Subject: [PATCH 111/127] Update hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala Co-authored-by: Lionel Parreaux --- hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala index 850509fc7..93824b7ab 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -307,7 +307,7 @@ final class LlirBuilder(tl: TraceLogger)(fresh: Fresh, fnUid: FreshInt, clsUid: def registerClasses(b: Block)(using ctx: Ctx)(using Raise, Scope): Ctx = b match case Define(cd @ ClsLikeDefn(_own, isym, sym, kind, _paramsOpt, auxParams, - parentSym, methods, privateFields, publicFields, preCtor, ctor), rest) => + parentSym, methods, privateFields, publicFields, preCtor, ctor), rest) => if !auxParams.isEmpty then errStop(msg"The class ${sym.nme} has auxiliary parameters, which are not yet supported") val c = bClsLikeDef(cd) From 94a0ee9f555b32ef0bf4e981719ee4283cc7213d Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 17:49:58 +0800 Subject: [PATCH 112/127] fix typo --- hkmc2/shared/src/test/mlscript/backlog/Lifter.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 14e67e152..66c378cf1 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -23,7 +23,7 @@ f(2) // We use the optional `symbol` parameter of `Select` to detect references to the // BlockMemberSymbol. But when this symbol is not present, there is no way to properly -// detect it. What do to? +// detect it. What to do? :expect 3 class A(x) with class B(y) with From d95d6a1938f8376b7a102f45bec0e95e217bca13 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:23:25 +0800 Subject: [PATCH 113/127] expose handler paths and fix some bugs --- .../scala/hkmc2/codegen/HandlerLowering.scala | 46 ++- .../src/main/scala/hkmc2/codegen/Lifter.scala | 10 +- .../main/scala/hkmc2/codegen/Lowering.scala | 10 +- .../scala/hkmc2/codegen/UsedVarAnalyzer.scala | 9 +- .../src/test/mlscript/codegen/Hygiene.mls | 2 +- .../src/test/mlscript/handlers/Effects.mls | 172 +++----- .../mlscript/handlers/EffectsInClasses.mls | 8 +- .../mlscript/handlers/RecursiveHandlers.mls | 38 +- .../test/mlscript/handlers/StackSafety.mls | 46 +-- .../src/test/mlscript/lifter/ClassInFun.mls | 177 ++++++++ .../test/mlscript/lifter/StackSafetyLift.mls | 388 +++++------------- 11 files changed, 428 insertions(+), 478 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 6ced5d28d..5eb042ff1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -54,6 +54,16 @@ object HandlerLowering: import HandlerLowering.* +class HandlerPaths(using Elaborator.State): + val runtimePath: Path = State.runtimeSymbol.asPath + val effectSigPath: Path = runtimePath.selN(Tree.Ident("EffectSig")).selN(Tree.Ident("class")) + val effectSigSym: ClassSymbol = State.effectSigSymbol + val contClsPath: Path = runtimePath.selN(Tree.Ident("FunctionContFrame")).selN(Tree.Ident("class")) + val retClsPath: Path = runtimePath.selN(Tree.Ident("Return")).selN(Tree.Ident("class")) + val retClsSym: ClassSymbol = State.returnClsSymbol + val mkEffectPath: Path = runtimePath.selN(Tree.Ident("mkEffect")) + val handleBlockImplPath: Path = runtimePath.selN(Tree.Ident("handleBlockImpl")) + class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def funcLikeHandlerCtx(ctorThis: Option[Path], isHandlerMtd: Bool, nme: Str) = @@ -69,14 +79,8 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def ctorCtx(ctorThis: Path, nme: Str) = funcLikeHandlerCtx(S(ctorThis), false, nme) private def handlerMtdCtx(nme: Str) = funcLikeHandlerCtx(N, true, nme) private def handlerCtx(using HandlerCtx): HandlerCtx = summon - private val runtimePath: Path = State.runtimeSymbol.asPath - private val effectSigPath: Path = runtimePath.selN(Tree.Ident("EffectSig")).selN(Tree.Ident("class")) - private val effectSigSym: ClassSymbol = State.effectSigSymbol - private val contClsPath: Path = runtimePath.selN(Tree.Ident("FunctionContFrame")).selN(Tree.Ident("class")) - private val retClsPath: Path = runtimePath.selN(Tree.Ident("Return")).selN(Tree.Ident("class")) - private val retClsSym: ClassSymbol = State.returnClsSymbol - private val mkEffectPath: Path = runtimePath.selN(Tree.Ident("mkEffect")) - private val handleBlockImplPath: Path = runtimePath.selN(Tree.Ident("handleBlockImpl")) + + private val paths = new HandlerPaths private def freshTmp(dbgNme: Str = "tmp") = new TempSymbol(N, dbgNme) @@ -391,7 +395,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): b match case Return(res, implct) => // In case res is effectful, it will be handled in translateBlock - Assign(tmp, res, Return(Instantiate(retClsPath, tmp.asPath :: Nil), implct)) + Assign(tmp, res, Return(Instantiate(paths.retClsPath, tmp.asPath :: Nil), implct)) case HandleBlockReturn(res) => Return(res, false) case _ => super.applyBlock(b) @@ -399,8 +403,8 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val handlerBody = translateBlock(prepareBody(h.body), HandlerCtx(false, false, false, true, s"Cont$$handleBlock$$${h.lhs.nme}$$", N, state => blockBuilder - .assignFieldN(state.res.contTrace.last, nextIdent, Instantiate(state.cls, Value.Lit(Tree.IntLit(state.uid)) :: Value.Lit(Tree.UnitLit(true)) :: Nil)) - .ret(PureCall(handleBlockImplPath, state.res :: h.lhs.asPath :: Nil)))) + .assignFieldN(state.res.contTrace.last, nextIdent, PureCall(state.cls, Value.Lit(Tree.IntLit(state.uid)) :: Nil)) + .ret(PureCall(paths.handleBlockImplPath, state.res :: h.lhs.asPath :: Nil)))) val handlerMtds = h.handlers.map: handler => val handleBlockSym = VarSymbol(Tree.Ident("handleBlock")) @@ -409,14 +413,14 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): translateBlock(handler.body, handlerMtdCtx(s"Cont$$handler$$${h.lhs.nme}$$${handler.sym.toLoc.fold("")(locToStr)}"))) FunDefn( S(h.cls), - handler.sym, handler.params, Return(PureCall(mkEffectPath, h.lhs.asPath :: lam :: Nil), false)) + handler.sym, handler.params, Return(PureCall(paths.mkEffectPath, h.cls.asPath :: lam :: Nil), false)) val clsDefn = ClsLikeDefn( N, // no owner h.cls, BlockMemberSymbol(h.cls.id.name, Nil), syntax.Cls, - N, + N, Nil, S(h.par), handlerMtds, Nil, Nil, Assign(freshTmp(), Call(Value.Ref(State.builtinOpsMap("super")), h.args.map(_.asArg))(true, true), End()), End()) // TODO: handle effect in super call // NOTE: the super call is inside the preCtor @@ -424,7 +428,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): val body = blockBuilder .define(clsDefn) - .assign(h.lhs, PureCall(clsDefn.sym.asPath, Nil)) + .assign(h.lhs, Instantiate(Value.Ref(clsDefn.sym), Nil)) .rest(handlerBody) val defn = FunDefn( @@ -453,13 +457,13 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): .assign(res, c) .ifthen( res.asPath, - Case.Cls(effectSigSym, effectSigPath), + Case.Cls(paths.effectSigSym, paths.effectSigPath), ReturnCont(res, uid) ) .chain(ResumptionPoint(res, uid, _)) .staticif(canRet, _.ifthen( res.asPath, - Case.Cls(retClsSym, retClsPath), + Case.Cls(paths.retClsSym, paths.retClsPath), blockBuilder .ret(if handlerCtx.shouldUnwrapRet then res.asPath.value else res.asPath) )) @@ -540,7 +544,7 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): syntax.Cls, S(PlainParamList(Param(FldFlags.empty, pcVar, N) :: Nil)), Nil, - S(contClsPath), + S(paths.contClsPath), resumeFnDef :: Nil, Nil, Nil, @@ -557,12 +561,12 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): .assign(res, c) .ifthen( res.asPath, - Case.Cls(effectSigSym, effectSigPath), + Case.Cls(paths.effectSigSym, paths.effectSigPath), handlerCtx.linkAndHandle(LinkState(res.asPath, clsSym.asPath, uid)) ) .staticif(canRet, _.ifthen( res.asPath, - Case.Cls(retClsSym, retClsPath), + Case.Cls(paths.retClsSym, paths.retClsPath), blockBuilder.ret(if handlerCtx.shouldUnwrapRet then res.asPath.value else res.asPath) )) .rest(applyBlock(rest)) @@ -570,6 +574,6 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): transform.applyBlock(b) - def translateTopLevel(b: Block): Block = - translateBlock(b, topLevelCtx(s"Cont$$topLevel$$BAD")) + def translateTopLevel(b: Block): (Block, HandlerPaths) = + (translateBlock(b, topLevelCtx(s"Cont$$topLevel$$BAD")), paths) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index a59e806a7..12bf96ea1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -112,18 +112,12 @@ object Lifter: case c: ClsLikeDefn => (c.k is syntax.Mod) || (c.k is syntax.Obj) case _ => false - // TODO: this is very unclean - def isContClassPth(p: Path)(using s: State) = p match - case Select(Select(Select(Value.Ref(s.globalThisSymbol), Tree.Ident("Predef")), - Tree.Ident("__Cont")), Tree.Ident("class")) => true - 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): +class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): import Lifter.* /** @@ -953,7 +947,7 @@ class Lifter(using State, Raise): // so we need to desugar them again val blk = LambdaRewriter.desugar(b) - val analyzer = UsedVarAnalyzer(blk) + val analyzer = UsedVarAnalyzer(blk, handlerPaths) val ctx = LifterCtx .withLocals(analyzer.findUsedLocals) .withDefns(analyzer.defnsMap) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index fda19523f..3a2e95da5 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -589,13 +589,15 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val stackSafe = config.stackSafety match case N => res case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) - val withHandlers = if lowerHandlers - then HandlerLowering().translateTopLevel(stackSafe) - else stackSafe + val (withHandlers, handlerPaths) = + if lowerHandlers then + val (b, paths) = HandlerLowering().translateTopLevel(stackSafe) + (b, S(paths)) + else (stackSafe, N) val flattened = withHandlers.flattened val lifted = - if lift then Lifter().transform(flattened) + if lift then Lifter(handlerPaths).transform(flattened) else flattened MergeMatchArmTransformer.applyBlock(lifted) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 3c8532c02..813ea7fb8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -17,7 +17,7 @@ import scala.collection.mutable.Map as MutMap * * Assumes the input trees have no lambdas. */ -class UsedVarAnalyzer(b: Block)(using State): +class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): import Lifter.* private case class DefnMetadata( @@ -112,6 +112,11 @@ class UsedVarAnalyzer(b: Block)(using State): def isModule(s: BlockMemberSymbol) = defnsMap.get(s) match case S(c: ClsLikeDefn) => c.k is syntax.Mod case _ => false + + def isContClassPth(p: Path) = handlerPaths match + case None => false + case Some(paths) => paths.contClsPath eq p + private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -323,7 +328,7 @@ class UsedVarAnalyzer(b: Block)(using State): // special case continuation classes defn match case c: ClsLikeDefn => c.parentPath match - case S(path) if Lifter.isContClassPth(path) => return + case S(path) if isContClassPth(path) => return // treat the continuation class as if it does not exist case _ => () case _ => () diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 0da844ff8..3aa46bece 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -204,7 +204,7 @@ Whoops.Whoops.g() :e Runtime //│ ╔══[ERROR] Name not found: Runtime -//│ ║ l.196: Runtime +//│ ║ l.205: Runtime //│ ╙── ^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 0b48955e2..02662aecc 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -165,18 +165,16 @@ if true do //│ tmp13 = super(); //│ } //│ perform(arg) { -//│ return runtime.mkEffect(h, (k) => { +//│ return runtime.mkEffect(this, (k) => { //│ return arg //│ }) //│ } -//│ toString() { return "Handler$h$(" + "" + ")"; } -//│ }; -//│ h = Handler$h$12(); -//│ Cont$24 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ toString() { return "Handler$h$"; } //│ }; //│ h = new Handler$h$12(); -//│ Cont$handleBlock$h$11 = function Cont$handleBlock$h$(pc1) { return new Cont$handleBlock$h$.class(pc1); }; +//│ Cont$handleBlock$h$11 = function Cont$handleBlock$h$(pc1) { +//│ return new Cont$handleBlock$h$.class(pc1); +//│ }; //│ Cont$handleBlock$h$11.class = class Cont$handleBlock$h$10 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp13; @@ -206,7 +204,7 @@ if true do //│ if (scrut === true) { //│ tmp12 = f(); //│ if (tmp12 instanceof runtime.EffectSig.class) { -//│ tmp12.contTrace.last.next = new Cont$handleBlock$h$11(0, null); +//│ tmp12.contTrace.last.next = Cont$handleBlock$h$11(0); //│ return runtime.handleBlockImpl(tmp12, h) //│ } //│ } else { @@ -393,14 +391,14 @@ foo(h) //│ JS (unsanitized): //│ let foo7, tmp20, handleBlock$20; //│ foo7 = function foo(h) { -//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, Cont$53; -//│ Cont$53 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, Cont$func$foo$Effects$_mls_L368_4_310$1; +//│ Cont$func$foo$Effects$_mls_L368_4_310$1 = function Cont$func$foo$Effects$_mls_L368_4_310$(pc1) { +//│ return new Cont$func$foo$Effects$_mls_L368_4_310$.class(pc1); //│ }; -//│ Cont$53.class = class Cont$50 extends globalThis.Predef.__Cont.class { +//│ Cont$func$foo$Effects$_mls_L368_4_310$1.class = class Cont$func$foo$Effects$_mls_L368_4_310$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp28; -//│ tmp28 = super(null, null); +//│ tmp28 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { @@ -422,17 +420,21 @@ foo(h) //│ scrut = true; //│ if (scrut === true) { //│ tmp22 = A10.f(); -//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (tmp22 instanceof runtime.EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(tmp22, this) +//│ tmp22.contTrace.last.next = this; +//│ tmp22.contTrace.last = this; +//│ return tmp22 //│ } //│ this.pc = 1; //│ continue contLoop; //│ } else { //│ tmp22 = A11.f(); -//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (tmp22 instanceof runtime.EffectSig.class) { //│ this.pc = 2; -//│ return globalThis.Predef.__appendInCont(tmp22, this) +//│ tmp22.contTrace.last.next = this; +//│ tmp22.contTrace.last = this; +//│ return tmp22 //│ } //│ this.pc = 2; //│ continue contLoop; @@ -444,17 +446,21 @@ foo(h) //│ scrut1 = false; //│ if (scrut1 === true) { //│ tmp23 = A12.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (tmp23 instanceof runtime.EffectSig.class) { //│ this.pc = 3; -//│ return globalThis.Predef.__appendInCont(tmp23, this) +//│ tmp23.contTrace.last.next = this; +//│ tmp23.contTrace.last = this; +//│ return tmp23 //│ } //│ this.pc = 3; //│ continue contLoop; //│ } else { //│ tmp23 = A13.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (tmp23 instanceof runtime.EffectSig.class) { //│ this.pc = 4; -//│ return globalThis.Predef.__appendInCont(tmp23, this) +//│ tmp23.contTrace.last.next = this; +//│ tmp23.contTrace.last = this; +//│ return tmp23 //│ } //│ this.pc = 4; //│ continue contLoop; @@ -470,9 +476,11 @@ foo(h) //│ } else if (this.pc === 6) { //│ b = tmp23; //│ tmp24 = A14(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (tmp24 instanceof runtime.EffectSig.class) { //│ this.pc = 5; -//│ return globalThis.Predef.__appendInCont(tmp24, this) +//│ tmp24.contTrace.last.next = this; +//│ tmp24.contTrace.last = this; +//│ return tmp24 //│ } //│ this.pc = 5; //│ continue contLoop; @@ -491,7 +499,7 @@ foo(h) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$foo$Effects$_mls_L368_4_310$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ A14 = function A() { //│ return 1 @@ -525,24 +533,24 @@ foo(h) //│ static toString() { return "A"; } //│ }; //│ tmp21 = runtime.safeCall(h.perform()); -//│ if (tmp21 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp21.tail.next = Cont$53(0); -//│ tmp21.tail = tmp21.tail.next; +//│ if (tmp21 instanceof runtime.EffectSig.class) { +//│ tmp21.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(0); +//│ tmp21.contTrace.last = tmp21.contTrace.last.next; //│ return tmp21 //│ } //│ scrut = true; //│ if (scrut === true) { //│ tmp22 = A10.f(); -//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp22.tail.next = Cont$53(1); -//│ tmp22.tail = tmp22.tail.next; +//│ if (tmp22 instanceof runtime.EffectSig.class) { +//│ tmp22.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(1); +//│ tmp22.contTrace.last = tmp22.contTrace.last.next; //│ return tmp22 //│ } //│ } else { //│ tmp22 = A11.f(); -//│ if (tmp22 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp22.tail.next = Cont$53(2); -//│ tmp22.tail = tmp22.tail.next; +//│ if (tmp22 instanceof runtime.EffectSig.class) { +//│ tmp22.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(2); +//│ tmp22.contTrace.last = tmp22.contTrace.last.next; //│ return tmp22 //│ } //│ } @@ -550,24 +558,24 @@ foo(h) //│ scrut1 = false; //│ if (scrut1 === true) { //│ tmp23 = A12.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp23.tail.next = Cont$53(3); -//│ tmp23.tail = tmp23.tail.next; +//│ if (tmp23 instanceof runtime.EffectSig.class) { +//│ tmp23.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(3); +//│ tmp23.contTrace.last = tmp23.contTrace.last.next; //│ return tmp23 //│ } //│ } else { //│ tmp23 = A13.f(); -//│ if (tmp23 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp23.tail.next = Cont$53(4); -//│ tmp23.tail = tmp23.tail.next; +//│ if (tmp23 instanceof runtime.EffectSig.class) { +//│ tmp23.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(4); +//│ tmp23.contTrace.last = tmp23.contTrace.last.next; //│ return tmp23 //│ } //│ } //│ b = tmp23; //│ tmp24 = A14(); -//│ if (tmp24 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp24.tail.next = Cont$53(5); -//│ tmp24.tail = tmp24.tail.next; +//│ if (tmp24 instanceof runtime.EffectSig.class) { +//│ tmp24.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(5); +//│ tmp24.contTrace.last = tmp24.contTrace.last.next; //│ return tmp24 //│ } //│ tmp25 = tmp24 * 100; @@ -576,68 +584,27 @@ foo(h) //│ return tmp27 + b //│ }; //│ handleBlock$20 = function handleBlock$() { -//│ let h, res3, Cont$53, Handler$h$22; -//│ Handler$h$22 = function Handler$h$() { -//│ return new Handler$h$.class(); -//│ }; -//│ Handler$h$22.class = class Handler$h$21 extends Eff1 { +//│ let h, res3, Cont$handleBlock$h$21, Handler$h$22; +//│ Handler$h$22 = class Handler$h$21 extends Eff1 { //│ constructor() { //│ let tmp21; //│ tmp21 = super(); //│ } //│ perform() { -//│ return globalThis.Predef.__mkEffect(h, (k) => { -//│ let res4, Cont$54; -//│ Cont$54 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); -//│ }; -//│ Cont$54.class = class Cont$51 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ let tmp21; -//│ tmp21 = super(null, null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 10) { -//│ res4 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 10) { -//│ if (res4 instanceof globalThis.Predef.__Return.class) { -//│ return res4 -//│ } -//│ this.pc = 11; -//│ continue contLoop; -//│ } else if (this.pc === 11) { -//│ return res4 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ res4 = runtime.safeCall(k(runtime.Unit)); -//│ if (res4 instanceof globalThis.Predef.__EffectSig.class) { -//│ res4.tail.next = Cont$54(10); -//│ res4.tail = res4.tail.next; -//│ return res4 -//│ } -//│ if (res4 instanceof globalThis.Predef.__Return.class) { -//│ return res4 -//│ } -//│ return res4 +//│ return runtime.mkEffect(this, (k) => { +//│ return runtime.safeCall(k(runtime.Unit)) //│ }) //│ } -//│ toString() { return "Handler$h$(" + "" + ")"; } +//│ toString() { return "Handler$h$"; } //│ }; -//│ h = Handler$h$22(); -//│ Cont$53 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ h = new Handler$h$22(); +//│ Cont$handleBlock$h$21 = function Cont$handleBlock$h$(pc1) { +//│ return new Cont$handleBlock$h$.class(pc1); //│ }; -//│ Cont$53.class = class Cont$52 extends globalThis.Predef.__Cont.class { +//│ Cont$handleBlock$h$21.class = class Cont$handleBlock$h$20 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp21; -//│ tmp21 = super(null, null); +//│ tmp21 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { @@ -646,31 +613,22 @@ foo(h) //│ } //│ contLoop: while (true) { //│ if (this.pc === 8) { -//│ if (res3 instanceof globalThis.Predef.__Return.class) { -//│ return res3 -//│ } -//│ this.pc = 9; -//│ continue contLoop; -//│ } else if (this.pc === 9) { //│ return res3 //│ } //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$handleBlock$h$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ res3 = foo7(h); -//│ if (res3 instanceof globalThis.Predef.__EffectSig.class) { -//│ res3.tail.next = Cont$53(8); -//│ return globalThis.Predef.__handleBlockImpl(res3, h) -//│ } -//│ if (res3 instanceof globalThis.Predef.__Return.class) { -//│ return res3 +//│ if (res3 instanceof runtime.EffectSig.class) { +//│ res3.contTrace.last.next = Cont$handleBlock$h$21(8); +//│ return runtime.handleBlockImpl(res3, h) //│ } //│ return res3 //│ }; //│ tmp20 = handleBlock$20(); -//│ if (tmp20 instanceof this.Predef.__EffectSig.class) { +//│ if (tmp20 instanceof runtime.EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } //│ tmp20 diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index 9ccb23c04..7ae61d890 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -19,7 +19,9 @@ class Lol(h) with //│ this.h = h; //│ let tmp, res, Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$1; //│ const this$Lol = this; -//│ Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$1 = function Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$(pc1) { return new Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$.class(pc1); }; +//│ Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$1 = function Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$(pc1) { +//│ return new Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$.class(pc1); +//│ }; //│ Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$1.class = class Cont$ctor$Lol$EffectsInClasses$_mls_L10_6_41$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp1; @@ -126,10 +128,10 @@ let oops = Lol(h) //│ > k //│ > b -//│ oops = Lol(Handler$h$()) +//│ oops = Lol(Handler$h$) oops.h -//│ = Handler$h$() +//│ = Handler$h$ let obj = {} diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 29582d45d..4c5cbda69 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -164,9 +164,11 @@ str //│ tmp12 = super(); //│ } //│ perform(arg) { -//│ return runtime.mkEffect(h1, (k) => { +//│ return runtime.mkEffect(this, (k) => { //│ let tmp12, tmp13, tmp14, Cont$handler$h1$RecursiveHandlers$_mls_L139_58_1301; -//│ Cont$handler$h1$RecursiveHandlers$_mls_L139_58_1301 = function Cont$handler$h1$RecursiveHandlers$_mls_L139_58_130(pc1) { return new Cont$handler$h1$RecursiveHandlers$_mls_L139_58_130.class(pc1); }; +//│ Cont$handler$h1$RecursiveHandlers$_mls_L139_58_1301 = function Cont$handler$h1$RecursiveHandlers$_mls_L139_58_130(pc1) { +//│ return new Cont$handler$h1$RecursiveHandlers$_mls_L139_58_130.class(pc1); +//│ }; //│ Cont$handler$h1$RecursiveHandlers$_mls_L139_58_1301.class = class Cont$handler$h1$RecursiveHandlers$_mls_L139_58_130 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp15; @@ -210,14 +212,12 @@ str //│ return runtime.Unit //│ }) //│ } -//│ toString() { return "Handler$h1$(" + "" + ")"; } -//│ }; -//│ h1 = Handler$h1$2(); -//│ Cont$16 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ toString() { return "Handler$h1$"; } //│ }; //│ h1 = new Handler$h1$2(); -//│ Cont$handleBlock$h1$2 = function Cont$handleBlock$h1$(pc1) { return new Cont$handleBlock$h1$.class(pc1); }; +//│ Cont$handleBlock$h1$2 = function Cont$handleBlock$h1$(pc1) { +//│ return new Cont$handleBlock$h1$.class(pc1); +//│ }; //│ Cont$handleBlock$h1$2.class = class Cont$handleBlock$h1$1 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp12; @@ -251,9 +251,11 @@ str //│ tmp13 = super(); //│ } //│ perform(arg) { -//│ return runtime.mkEffect(h2, (k) => { +//│ return runtime.mkEffect(this, (k) => { //│ let tmp13, tmp14, tmp15, tmp16, tmp17, Cont$handler$h2$RecursiveHandlers$_mls_L139_165_2491; -//│ Cont$handler$h2$RecursiveHandlers$_mls_L139_165_2491 = function Cont$handler$h2$RecursiveHandlers$_mls_L139_165_249(pc1) { return new Cont$handler$h2$RecursiveHandlers$_mls_L139_165_249.class(pc1); }; +//│ Cont$handler$h2$RecursiveHandlers$_mls_L139_165_2491 = function Cont$handler$h2$RecursiveHandlers$_mls_L139_165_249(pc1) { +//│ return new Cont$handler$h2$RecursiveHandlers$_mls_L139_165_249.class(pc1); +//│ }; //│ Cont$handler$h2$RecursiveHandlers$_mls_L139_165_2491.class = class Cont$handler$h2$RecursiveHandlers$_mls_L139_165_249 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp18; @@ -300,14 +302,12 @@ str //│ return runtime.Unit //│ }) //│ } -//│ toString() { return "Handler$h2$(" + "" + ")"; } -//│ }; -//│ h2 = Handler$h2$2(); -//│ Cont$17 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ toString() { return "Handler$h2$"; } //│ }; //│ h2 = new Handler$h2$2(); -//│ Cont$handleBlock$h2$2 = function Cont$handleBlock$h2$(pc1) { return new Cont$handleBlock$h2$.class(pc1); }; +//│ Cont$handleBlock$h2$2 = function Cont$handleBlock$h2$(pc1) { +//│ return new Cont$handleBlock$h2$.class(pc1); +//│ }; //│ Cont$handleBlock$h2$2.class = class Cont$handleBlock$h2$1 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp13; @@ -341,19 +341,19 @@ str //│ }; //│ tmp12 = runtime.safeCall(h2.perform(runtime.Unit)); //│ if (tmp12 instanceof runtime.EffectSig.class) { -//│ tmp12.contTrace.last.next = new Cont$handleBlock$h2$2(0, null); +//│ tmp12.contTrace.last.next = Cont$handleBlock$h2$2(0); //│ return runtime.handleBlockImpl(tmp12, h2) //│ } //│ res7 = runtime.safeCall(h1.perform(runtime.Unit)); //│ if (res7 instanceof runtime.EffectSig.class) { -//│ res7.contTrace.last.next = new Cont$handleBlock$h2$2(1, null); +//│ res7.contTrace.last.next = Cont$handleBlock$h2$2(1); //│ return runtime.handleBlockImpl(res7, h2) //│ } //│ return res7 //│ }; //│ tmp11 = handleBlock$8(); //│ if (tmp11 instanceof runtime.EffectSig.class) { -//│ tmp11.contTrace.last.next = new Cont$handleBlock$h1$2(4, null); +//│ tmp11.contTrace.last.next = Cont$handleBlock$h1$2(4); //│ return runtime.handleBlockImpl(tmp11, h1) //│ } //│ if (tmp11 instanceof runtime.Return.class) { diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index d8d9121e3..c435fdad9 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -23,7 +23,9 @@ hi(0) //│ let hi, res, handleBlock$; //│ hi = function hi(n) { //│ let scrut, tmp, stackDelayRes, Cont$func$hi$StackSafety$_mls_L18_4_47$1; -//│ Cont$func$hi$StackSafety$_mls_L18_4_47$1 = function Cont$func$hi$StackSafety$_mls_L18_4_47$(pc1) { return new Cont$func$hi$StackSafety$_mls_L18_4_47$.class(pc1); }; +//│ Cont$func$hi$StackSafety$_mls_L18_4_47$1 = function Cont$func$hi$StackSafety$_mls_L18_4_47$(pc1) { +//│ return new Cont$func$hi$StackSafety$_mls_L18_4_47$.class(pc1); +//│ }; //│ Cont$func$hi$StackSafety$_mls_L18_4_47$1.class = class Cont$func$hi$StackSafety$_mls_L18_4_47$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp1; @@ -77,19 +79,17 @@ hi(0) //│ tmp = super(); //│ } //│ perform() { -//│ return runtime.mkEffect(stackHandler, (resume) => { +//│ return runtime.mkEffect(this, (resume) => { //│ runtime.stackOffset = runtime.stackDepth; //│ return resume() //│ }) //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ stackHandler = StackDelay$1(); -//│ Cont$3 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ toString() { return "StackDelay$"; } //│ }; //│ stackHandler = new StackDelay$1(); -//│ Cont$handleBlock$stackHandler$1 = function Cont$handleBlock$stackHandler$(pc1) { return new Cont$handleBlock$stackHandler$.class(pc1); }; +//│ Cont$handleBlock$stackHandler$1 = function Cont$handleBlock$stackHandler$(pc1) { +//│ return new Cont$handleBlock$stackHandler$.class(pc1); +//│ }; //│ Cont$handleBlock$stackHandler$1.class = class Cont$handleBlock$stackHandler$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp; @@ -115,7 +115,7 @@ hi(0) //│ runtime.stackHandler = stackHandler; //│ res1 = hi(0); //│ if (res1 instanceof runtime.EffectSig.class) { -//│ res1.contTrace.last.next = new Cont$handleBlock$stackHandler$1(2, null); +//│ res1.contTrace.last.next = Cont$handleBlock$stackHandler$1(2); //│ return runtime.handleBlockImpl(res1, stackHandler) //│ } //│ return res1 @@ -141,9 +141,11 @@ sum(10000) //│ JS (unsanitized): //│ let sum1, res1, handleBlock$1; //│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, curDepth, stackDelayRes, Cont$func$sum$StackSafety$_mls_L132_4_57$1; -//│ Cont$func$sum$StackSafety$_mls_L132_4_57$1 = function Cont$func$sum$StackSafety$_mls_L132_4_57$(pc1) { return new Cont$func$sum$StackSafety$_mls_L132_4_57$.class(pc1); }; -//│ Cont$func$sum$StackSafety$_mls_L132_4_57$1.class = class Cont$func$sum$StackSafety$_mls_L132_4_57$ extends runtime.FunctionContFrame.class { +//│ let scrut, tmp, tmp1, curDepth, stackDelayRes, Cont$func$sum$StackSafety$_mls_L136_4_57$1; +//│ Cont$func$sum$StackSafety$_mls_L136_4_57$1 = function Cont$func$sum$StackSafety$_mls_L136_4_57$(pc1) { +//│ return new Cont$func$sum$StackSafety$_mls_L136_4_57$.class(pc1); +//│ }; +//│ Cont$func$sum$StackSafety$_mls_L136_4_57$1.class = class Cont$func$sum$StackSafety$_mls_L136_4_57$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp2; //│ tmp2 = super(null); @@ -184,12 +186,12 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$func$sum$StackSafety$_mls_L132_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$sum$StackSafety$_mls_L136_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ curDepth = runtime.stackDepth; //│ stackDelayRes = runtime.checkDepth(); //│ if (stackDelayRes instanceof runtime.EffectSig.class) { -//│ stackDelayRes.contTrace.last.next = new Cont$func$sum$StackSafety$_mls_L132_4_57$1.class(0); +//│ stackDelayRes.contTrace.last.next = new Cont$func$sum$StackSafety$_mls_L136_4_57$1.class(0); //│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; //│ return stackDelayRes //│ } @@ -201,7 +203,7 @@ sum(10000) //│ runtime.stackDepth = runtime.stackDepth + 1; //│ tmp1 = sum1(tmp); //│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = new Cont$func$sum$StackSafety$_mls_L132_4_57$1.class(1); +//│ tmp1.contTrace.last.next = new Cont$func$sum$StackSafety$_mls_L136_4_57$1.class(1); //│ tmp1.contTrace.last = tmp1.contTrace.last.next; //│ return tmp1 //│ } @@ -217,19 +219,17 @@ sum(10000) //│ tmp = super(); //│ } //│ perform() { -//│ return runtime.mkEffect(stackHandler, (resume) => { +//│ return runtime.mkEffect(this, (resume) => { //│ runtime.stackOffset = runtime.stackDepth; //│ return resume() //│ }) //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } -//│ }; -//│ stackHandler = StackDelay$2(); -//│ Cont$6 = function Cont$(pc1) { -//│ return new Cont$.class(pc1); +//│ toString() { return "StackDelay$"; } //│ }; //│ stackHandler = new StackDelay$2(); -//│ Cont$handleBlock$stackHandler$2 = function Cont$handleBlock$stackHandler$(pc1) { return new Cont$handleBlock$stackHandler$.class(pc1); }; +//│ Cont$handleBlock$stackHandler$2 = function Cont$handleBlock$stackHandler$(pc1) { +//│ return new Cont$handleBlock$stackHandler$.class(pc1); +//│ }; //│ Cont$handleBlock$stackHandler$2.class = class Cont$handleBlock$stackHandler$1 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ let tmp; @@ -255,7 +255,7 @@ sum(10000) //│ runtime.stackHandler = stackHandler; //│ res2 = sum1(10000); //│ if (res2 instanceof runtime.EffectSig.class) { -//│ res2.contTrace.last.next = new Cont$handleBlock$stackHandler$2(3, null); +//│ res2.contTrace.last.next = Cont$handleBlock$stackHandler$2(3); //│ return runtime.handleBlockImpl(res2, stackHandler) //│ } //│ return res2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index ff02416f7..16d0264a0 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -3,6 +3,7 @@ // make sure class fields are discriminated properly for immutable captures when supported :effectHandlers +:sjs abstract class Effect with fun perform() handle h = Effect with @@ -11,6 +12,182 @@ fun f() = h.perform() 1 f() + f() + f() +//│ JS (unsanitized): +//│ let f, Effect1, tmp, handleBlock$, Cont$func$f$ClassInFun$_mls_L7_95_118$1, Cont$handleBlock$h$1, Handler$h$1, lambda, f$, Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor, Cont$func$f$ClassInFun$_mls_L7_95_118$$, Cont$handleBlock$h$$ctor, Cont$handleBlock$h$$, lambda$; +//│ Effect1 = class Effect { +//│ constructor() {} +//│ toString() { return "Effect"; } +//│ }; +//│ lambda$ = function lambda$(Handler$h$$instance, k) { +//│ return runtime.safeCall(k()) +//│ }; +//│ lambda = function lambda(Handler$h$$instance) { +//│ return (k) => { +//│ return lambda$(Handler$h$$instance, k) +//│ } +//│ }; +//│ Handler$h$1 = class Handler$h$ extends Effect1 { +//│ constructor() { +//│ let tmp1; +//│ tmp1 = super(); +//│ } +//│ perform() { +//│ let lambda$this; +//│ lambda$this = runtime.safeCall(lambda(this)); +//│ return runtime.mkEffect(this, lambda$this) +//│ } +//│ toString() { return "Handler$h$"; } +//│ }; +//│ Cont$handleBlock$h$$ = function Cont$handleBlock$h$$(h$0, tmp$1, tmp$2, tmp$3, tmp$4, pc) { +//│ let tmp1; +//│ tmp1 = new Cont$handleBlock$h$1.class(pc); +//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) +//│ }; +//│ Cont$handleBlock$h$$ctor = function Cont$handleBlock$h$$ctor(h$0, tmp$1, tmp$2, tmp$3, tmp$4) { +//│ return (pc) => { +//│ let tmp1; +//│ tmp1 = new Cont$handleBlock$h$1.class(pc); +//│ return tmp1(h$0, tmp$1, tmp$2, tmp$3, tmp$4) +//│ } +//│ }; +//│ Cont$handleBlock$h$1 = function Cont$handleBlock$h$(pc1) { +//│ return (h$01, tmp$11, tmp$21, tmp$31, tmp$41) => { +//│ return new Cont$handleBlock$h$.class(pc1)(h$01, tmp$11, tmp$21, tmp$31, tmp$41); +//│ } +//│ }; +//│ Cont$handleBlock$h$1.class = class Cont$handleBlock$h$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (h$0, tmp$1, tmp$2, tmp$3, tmp$4) => { +//│ let tmp1; +//│ tmp1 = super(null); +//│ this.pc = pc; +//│ this.h$0 = h$0; +//│ this.tmp$1 = tmp$1; +//│ this.tmp$2 = tmp$2; +//│ this.tmp$3 = tmp$3; +//│ this.tmp$4 = tmp$4; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 1) { +//│ this.tmp$1 = value$; +//│ } else if (this.pc === 2) { +//│ this.tmp$2 = value$; +//│ } else if (this.pc === 3) { +//│ this.tmp$4 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 1) { +//│ this.tmp$2 = f$(this.h$0); +//│ if (this.tmp$2 instanceof runtime.EffectSig.class) { +//│ this.pc = 2; +//│ this.tmp$2.contTrace.last.next = this; +//│ this.tmp$2.contTrace.last = this; +//│ return this.tmp$2 +//│ } +//│ this.pc = 2; +//│ continue contLoop; +//│ } else if (this.pc === 2) { +//│ this.tmp$3 = this.tmp$1 + this.tmp$2; +//│ this.tmp$4 = f$(this.h$0); +//│ if (this.tmp$4 instanceof runtime.EffectSig.class) { +//│ this.pc = 3; +//│ this.tmp$4.contTrace.last.next = this; +//│ this.tmp$4.contTrace.last = this; +//│ return this.tmp$4 +//│ } +//│ this.pc = 3; +//│ continue contLoop; +//│ } else if (this.pc === 3) { +//│ return this.tmp$3 + this.tmp$4 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$handleBlock$h$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ = function Cont$func$f$ClassInFun$_mls_L7_95_118$$(tmp$0, pc) { +//│ let tmp1; +//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); +//│ return tmp1(tmp$0) +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor = function Cont$func$f$ClassInFun$_mls_L7_95_118$$ctor(tmp$0) { +//│ return (pc) => { +//│ let tmp1; +//│ tmp1 = new Cont$func$f$ClassInFun$_mls_L7_95_118$1.class(pc); +//│ return tmp1(tmp$0) +//│ } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1 = function Cont$func$f$ClassInFun$_mls_L7_95_118$(pc1) { +//│ return (tmp$01) => { +//│ return new Cont$func$f$ClassInFun$_mls_L7_95_118$.class(pc1)(tmp$01); +//│ } +//│ }; +//│ Cont$func$f$ClassInFun$_mls_L7_95_118$1.class = class Cont$func$f$ClassInFun$_mls_L7_95_118$ extends runtime.FunctionContFrame.class { +//│ constructor(pc) { +//│ return (tmp$0) => { +//│ let tmp1; +//│ tmp1 = super(null); +//│ this.pc = pc; +//│ this.tmp$0 = tmp$0; +//│ return this; +//│ } +//│ } +//│ resume(value$) { +//│ if (this.pc === 0) { +//│ this.tmp$0 = value$; +//│ } +//│ contLoop: while (true) { +//│ if (this.pc === 0) { +//│ return 1 +//│ } +//│ break; +//│ } +//│ } +//│ toString() { return "Cont$func$f$ClassInFun$_mls_L7_95_118$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ }; +//│ f$ = function f$(h) { +//│ let tmp1; +//│ tmp1 = runtime.safeCall(h.perform()); +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$func$f$ClassInFun$_mls_L7_95_118$$(tmp1, 0); +//│ tmp1.contTrace.last = tmp1.contTrace.last.next; +//│ return tmp1 +//│ } +//│ return 1 +//│ }; +//│ f = function f(h) { +//│ return () => { +//│ return f$(h) +//│ } +//│ }; +//│ handleBlock$ = function handleBlock$() { +//│ let h, tmp1, tmp2, tmp3, tmp4; +//│ h = new Handler$h$1(); +//│ tmp1 = f$(h); +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 1); +//│ return runtime.handleBlockImpl(tmp1, h) +//│ } +//│ tmp2 = f$(h); +//│ if (tmp2 instanceof runtime.EffectSig.class) { +//│ tmp2.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 2); +//│ return runtime.handleBlockImpl(tmp2, h) +//│ } +//│ tmp3 = tmp1 + tmp2; +//│ tmp4 = f$(h); +//│ if (tmp4 instanceof runtime.EffectSig.class) { +//│ tmp4.contTrace.last.next = Cont$handleBlock$h$$(h, tmp1, tmp2, tmp3, tmp4, 3); +//│ return runtime.handleBlockImpl(tmp4, h) +//│ } +//│ return tmp3 + tmp4 +//│ }; +//│ tmp = handleBlock$(); +//│ if (tmp instanceof runtime.EffectSig.class) { +//│ throw new this.Error("Unhandled effects"); +//│ } +//│ tmp //│ = 3 :expect 1 diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 0431acdb5..472b6668a 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -21,29 +21,29 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, res, Cont$3, handleBlock$, Cont$4, Cont$5, StackDelay$1, lambda, Cont$$ctor, Cont$$, Cont$$ctor1, Cont$$1, StackDelay$$ctor, StackDelay$$, lambda$, Cont$$ctor2, Cont$$2, handleBlock$$capture1; -//│ Cont$$ = function Cont$$(n$0, scrut$1, tmp$2, stackDelayRes$3, pc) { +//│ let hi, res, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1, handleBlock$, Cont$handleBlock$stackHandler$1, StackDelay$1, lambda, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor, Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$, Cont$handleBlock$stackHandler$$ctor, Cont$handleBlock$stackHandler$$, lambda$; +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n$0, scrut$1, tmp$2, stackDelayRes$3, pc) { //│ let tmp; -//│ tmp = new Cont$3.class(pc); +//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) //│ }; -//│ Cont$$ctor = function Cont$$ctor(n$0, scrut$1, tmp$2, stackDelayRes$3) { +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$ctor(n$0, scrut$1, tmp$2, stackDelayRes$3) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$3.class(pc); +//│ tmp = new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, stackDelayRes$3) //│ } //│ }; -//│ Cont$3 = function Cont$(pc1) { +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1 = function Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(pc1) { //│ return (n$01, scrut$11, tmp$21, stackDelayRes$31) => { -//│ return new Cont$.class(pc1)(n$01, scrut$11, tmp$21, stackDelayRes$31); +//│ return new Cont$func$hi$StackSafetyLift$_mls_L19_4_47$.class(pc1)(n$01, scrut$11, tmp$21, stackDelayRes$31); //│ } //│ }; -//│ Cont$3.class = class Cont$ extends globalThis.Predef.__Cont.class { +//│ Cont$func$hi$StackSafetyLift$_mls_L19_4_47$1.class = class Cont$func$hi$StackSafetyLift$_mls_L19_4_47$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (n$0, scrut$1, tmp$2, stackDelayRes$3) => { //│ let tmp; -//│ tmp = super(null, null); +//│ tmp = super(null); //│ this.pc = pc; //│ this.n$0 = n$0; //│ this.scrut$1 = scrut$1; @@ -63,7 +63,7 @@ hi(0) //│ return 0 //│ } else { //│ this.tmp$2 = this.n$0 - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; //│ return hi(this.tmp$2) //│ } //│ this.pc = 1; @@ -74,14 +74,14 @@ hi(0) //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$hi$StackSafetyLift$_mls_L19_4_47$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ hi = function hi(n) { //│ let scrut, tmp, stackDelayRes; -//│ stackDelayRes = globalThis.Predef.checkDepth(); -//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { -//│ stackDelayRes.tail.next = Cont$$(n, scrut, tmp, stackDelayRes, 0); -//│ stackDelayRes.tail = stackDelayRes.tail.next; +//│ stackDelayRes = runtime.checkDepth(); +//│ if (stackDelayRes instanceof runtime.EffectSig.class) { +//│ stackDelayRes.contTrace.last.next = Cont$func$hi$StackSafetyLift$_mls_L19_4_47$$(n, scrut, tmp, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; //│ return stackDelayRes //│ } //│ scrut = n == 0; @@ -89,131 +89,53 @@ hi(0) //│ return 0 //│ } else { //│ tmp = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; //│ return hi(tmp) //│ } //│ }; -//│ StackDelay$$ = function StackDelay$$(handleBlock$$capture$0) { -//│ let tmp; -//│ tmp = new StackDelay$1.class(); -//│ return tmp(handleBlock$$capture$0) -//│ }; -//│ StackDelay$$ctor = function StackDelay$$ctor(handleBlock$$capture$0) { -//│ return () => { -//│ let tmp; -//│ tmp = new StackDelay$1.class(); -//│ return tmp(handleBlock$$capture$0) -//│ } -//│ }; -//│ Cont$$2 = function Cont$$(StackDelay$$instance$1, res$0, pc) { -//│ let tmp; -//│ tmp = new Cont$5.class(pc); -//│ return tmp(StackDelay$$instance$1, res$0) -//│ }; -//│ Cont$$ctor2 = function Cont$$ctor(StackDelay$$instance$1, res$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$5.class(pc); -//│ return tmp(StackDelay$$instance$1, res$0) -//│ } -//│ }; -//│ Cont$5 = function Cont$(pc1) { -//│ return (StackDelay$$instance$11, res$01) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$11, res$01); -//│ } -//│ }; -//│ Cont$5.class = class Cont$1 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (StackDelay$$instance$1, res$0) => { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ this.StackDelay$$instance$1 = StackDelay$$instance$1; -//│ this.res$0 = res$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 4) { -//│ this.res$0 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 4) { -//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { -//│ return this.res$0 -//│ } -//│ this.pc = 5; -//│ continue contLoop; -//│ } else if (this.pc === 5) { -//│ return this.res$0 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; //│ lambda$ = function lambda$(StackDelay$$instance, resume) { -//│ let res1; -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ res1 = resume(); -//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$$2(StackDelay$$instance, res1, 4); -//│ res1.tail = res1.tail.next; -//│ return res1 -//│ } -//│ if (res1 instanceof globalThis.Predef.__Return.class) { -//│ return res1 -//│ } -//│ return res1 +//│ runtime.stackOffset = runtime.stackDepth; +//│ return resume() //│ }; //│ lambda = function lambda(StackDelay$$instance) { //│ return (resume) => { //│ return lambda$(StackDelay$$instance, resume) //│ } //│ }; -//│ StackDelay$1 = function StackDelay$() { -//│ return (handleBlock$$capture$01) => { -//│ return new StackDelay$.class()(handleBlock$$capture$01); -//│ } -//│ }; -//│ StackDelay$1.class = class StackDelay$ extends globalThis.Predef.__StackDelay { +//│ StackDelay$1 = class StackDelay$ extends runtime.StackDelay { //│ constructor() { -//│ return (handleBlock$$capture$0) => { -//│ let tmp; -//│ tmp = super(); -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } +//│ let tmp; +//│ tmp = super(); //│ } //│ perform() { //│ let lambda$this; //│ lambda$this = runtime.safeCall(lambda(this)); -//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ return runtime.mkEffect(this, lambda$this) //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ toString() { return "StackDelay$"; } //│ }; -//│ Cont$$1 = function Cont$$(res$0, pc) { +//│ Cont$handleBlock$stackHandler$$ = function Cont$handleBlock$stackHandler$$(res$0, pc) { //│ let tmp; -//│ tmp = new Cont$4.class(pc); +//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); //│ return tmp(res$0) //│ }; -//│ Cont$$ctor1 = function Cont$$ctor(res$0) { +//│ Cont$handleBlock$stackHandler$$ctor = function Cont$handleBlock$stackHandler$$ctor(res$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$4.class(pc); +//│ tmp = new Cont$handleBlock$stackHandler$1.class(pc); //│ return tmp(res$0) //│ } //│ }; -//│ Cont$4 = function Cont$(pc1) { +//│ Cont$handleBlock$stackHandler$1 = function Cont$handleBlock$stackHandler$(pc1) { //│ return (res$01) => { -//│ return new Cont$.class(pc1)(res$01); +//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); //│ } //│ }; -//│ Cont$4.class = class Cont$2 extends globalThis.Predef.__Cont.class { +//│ Cont$handleBlock$stackHandler$1.class = class Cont$handleBlock$stackHandler$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (res$0) => { //│ let tmp; -//│ tmp = super(null, null); +//│ tmp = super(null); //│ this.pc = pc; //│ this.res$0 = res$0; //│ return this; @@ -225,52 +147,33 @@ hi(0) //│ } //│ contLoop: while (true) { //│ if (this.pc === 2) { -//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { -//│ return this.res$0 -//│ } -//│ this.pc = 3; -//│ continue contLoop; -//│ } else if (this.pc === 3) { //│ return this.res$0 //│ } //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$$capture1 = function handleBlock$$capture(stackHandler0$1) { -//│ return new handleBlock$$capture.class(stackHandler0$1); -//│ }; -//│ handleBlock$$capture1.class = class handleBlock$$capture { -//│ constructor(stackHandler0$) { -//│ this.stackHandler0$ = stackHandler0$; -//│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ")"; } +//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ handleBlock$ = function handleBlock$() { -//│ let res1, capture; -//│ capture = new handleBlock$$capture1(null); -//│ capture.stackHandler0$ = StackDelay$$(capture); -//│ globalThis.Predef.__stackLimit = 5; -//│ globalThis.Predef.__stackOffset = 0; -//│ globalThis.Predef.__stackDepth = 1; -//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; +//│ let stackHandler, res1; +//│ stackHandler = new StackDelay$1(); +//│ runtime.stackLimit = 5; +//│ runtime.stackOffset = 0; +//│ runtime.stackDepth = 1; +//│ runtime.stackHandler = stackHandler; //│ res1 = hi(0); -//│ if (res1 instanceof globalThis.Predef.__EffectSig.class) { -//│ res1.tail.next = Cont$$1(res1, 2); -//│ return globalThis.Predef.__handleBlockImpl(res1, capture.stackHandler0$) -//│ } -//│ if (res1 instanceof globalThis.Predef.__Return.class) { -//│ return res1 +//│ if (res1 instanceof runtime.EffectSig.class) { +//│ res1.contTrace.last.next = Cont$handleBlock$stackHandler$$(res1, 2); +//│ return runtime.handleBlockImpl(res1, stackHandler) //│ } //│ return res1 //│ }; //│ res = handleBlock$(); -//│ if (res instanceof this.Predef.__EffectSig.class) { +//│ if (res instanceof runtime.EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ this.Predef.__stackDepth = 0; -//│ this.Predef.__stackHandler = null; +//│ runtime.stackDepth = 0; +//│ runtime.stackHandler = null; //│ res //│ = 0 @@ -284,29 +187,29 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, Cont$9, handleBlock$1, Cont$10, Cont$11, StackDelay$3, lambda1, Cont$$ctor3, Cont$$3, Cont$$ctor4, Cont$$4, StackDelay$$ctor1, StackDelay$$1, lambda$1, Cont$$ctor5, Cont$$5, handleBlock$$capture3; -//│ Cont$$3 = function Cont$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { +//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; +//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { //│ let tmp; -//│ tmp = new Cont$9.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ }; -//│ Cont$$ctor3 = function Cont$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { +//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$9.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ } //│ }; -//│ Cont$9 = function Cont$(pc1) { +//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$(pc1) { //│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { -//│ return new Cont$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); +//│ return new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); //│ } //│ }; -//│ Cont$9.class = class Cont$6 extends globalThis.Predef.__Cont.class { +//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L213_4_57$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { //│ let tmp; -//│ tmp = super(null, null); +//│ tmp = super(null); //│ this.pc = pc; //│ this.n$0 = n$0; //│ this.scrut$1 = scrut$1; @@ -330,11 +233,13 @@ sum(10000) //│ return 0 //│ } else { //│ this.tmp$2 = this.n$0 - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; //│ this.tmp$3 = sum1(this.tmp$2); -//│ if (this.tmp$3 instanceof globalThis.Predef.__EffectSig.class) { +//│ if (this.tmp$3 instanceof runtime.EffectSig.class) { //│ this.pc = 1; -//│ return globalThis.Predef.__appendInCont(this.tmp$3, this) +//│ this.tmp$3.contTrace.last.next = this; +//│ this.tmp$3.contTrace.last = this; +//│ return this.tmp$3 //│ } //│ this.pc = 1; //│ continue contLoop; @@ -344,21 +249,21 @@ sum(10000) //│ } else if (this.pc === 2) { //│ break contLoop; //│ } else if (this.pc === 1) { -//│ this.tmp$3 = globalThis.Predef.resetDepth(this.tmp$3, this.curDepth$4); +//│ this.tmp$3 = runtime.resetDepth(this.tmp$3, this.curDepth$4); //│ return this.n$0 + this.tmp$3 //│ } //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L213_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ sum1 = function sum(n) { //│ let scrut, tmp, tmp1, curDepth, stackDelayRes; -//│ curDepth = globalThis.Predef.__stackDepth; -//│ stackDelayRes = globalThis.Predef.checkDepth(); -//│ if (stackDelayRes instanceof globalThis.Predef.__EffectSig.class) { -//│ stackDelayRes.tail.next = Cont$$3(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); -//│ stackDelayRes.tail = stackDelayRes.tail.next; +//│ curDepth = runtime.stackDepth; +//│ stackDelayRes = runtime.checkDepth(); +//│ if (stackDelayRes instanceof runtime.EffectSig.class) { +//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; //│ return stackDelayRes //│ } //│ scrut = n == 0; @@ -366,138 +271,60 @@ sum(10000) //│ return 0 //│ } else { //│ tmp = n - 1; -//│ globalThis.Predef.__stackDepth = globalThis.Predef.__stackDepth + 1; +//│ runtime.stackDepth = runtime.stackDepth + 1; //│ tmp1 = sum1(tmp); -//│ if (tmp1 instanceof globalThis.Predef.__EffectSig.class) { -//│ tmp1.tail.next = Cont$$3(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); -//│ tmp1.tail = tmp1.tail.next; +//│ if (tmp1 instanceof runtime.EffectSig.class) { +//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); +//│ tmp1.contTrace.last = tmp1.contTrace.last.next; //│ return tmp1 //│ } -//│ tmp1 = globalThis.Predef.resetDepth(tmp1, curDepth); +//│ tmp1 = runtime.resetDepth(tmp1, curDepth); //│ return n + tmp1 //│ } //│ }; -//│ StackDelay$$1 = function StackDelay$$(handleBlock$$capture$0) { -//│ let tmp; -//│ tmp = new StackDelay$3.class(); -//│ return tmp(handleBlock$$capture$0) -//│ }; -//│ StackDelay$$ctor1 = function StackDelay$$ctor(handleBlock$$capture$0) { -//│ return () => { -//│ let tmp; -//│ tmp = new StackDelay$3.class(); -//│ return tmp(handleBlock$$capture$0) -//│ } -//│ }; -//│ Cont$$5 = function Cont$$(StackDelay$$instance$1, res$0, pc) { -//│ let tmp; -//│ tmp = new Cont$11.class(pc); -//│ return tmp(StackDelay$$instance$1, res$0) -//│ }; -//│ Cont$$ctor5 = function Cont$$ctor(StackDelay$$instance$1, res$0) { -//│ return (pc) => { -//│ let tmp; -//│ tmp = new Cont$11.class(pc); -//│ return tmp(StackDelay$$instance$1, res$0) -//│ } -//│ }; -//│ Cont$11 = function Cont$(pc1) { -//│ return (StackDelay$$instance$11, res$01) => { -//│ return new Cont$.class(pc1)(StackDelay$$instance$11, res$01); -//│ } -//│ }; -//│ Cont$11.class = class Cont$7 extends globalThis.Predef.__Cont.class { -//│ constructor(pc) { -//│ return (StackDelay$$instance$1, res$0) => { -//│ let tmp; -//│ tmp = super(null, null); -//│ this.pc = pc; -//│ this.StackDelay$$instance$1 = StackDelay$$instance$1; -//│ this.res$0 = res$0; -//│ return this; -//│ } -//│ } -//│ resume(value$) { -//│ if (this.pc === 5) { -//│ this.res$0 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 5) { -//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { -//│ return this.res$0 -//│ } -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ return this.res$0 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; //│ lambda$1 = function lambda$(StackDelay$$instance, resume) { -//│ let res2; -//│ globalThis.Predef.__stackOffset = globalThis.Predef.__stackDepth; -//│ res2 = resume(); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$$5(StackDelay$$instance, res2, 5); -//│ res2.tail = res2.tail.next; -//│ return res2 -//│ } -//│ if (res2 instanceof globalThis.Predef.__Return.class) { -//│ return res2 -//│ } -//│ return res2 +//│ runtime.stackOffset = runtime.stackDepth; +//│ return resume() //│ }; //│ lambda1 = function lambda(StackDelay$$instance) { //│ return (resume) => { //│ return lambda$1(StackDelay$$instance, resume) //│ } //│ }; -//│ StackDelay$3 = function StackDelay$() { -//│ return (handleBlock$$capture$01) => { -//│ return new StackDelay$.class()(handleBlock$$capture$01); -//│ } -//│ }; -//│ StackDelay$3.class = class StackDelay$2 extends globalThis.Predef.__StackDelay { +//│ StackDelay$3 = class StackDelay$2 extends runtime.StackDelay { //│ constructor() { -//│ return (handleBlock$$capture$0) => { -//│ let tmp; -//│ tmp = super(); -//│ this.handleBlock$$capture$0 = handleBlock$$capture$0; -//│ return this; -//│ } +//│ let tmp; +//│ tmp = super(); //│ } //│ perform() { //│ let lambda$this; //│ lambda$this = runtime.safeCall(lambda1(this)); -//│ return globalThis.Predef.__mkEffect(this.handleBlock$$capture$0.stackHandler0$, lambda$this) +//│ return runtime.mkEffect(this, lambda$this) //│ } -//│ toString() { return "StackDelay$(" + "" + ")"; } +//│ toString() { return "StackDelay$"; } //│ }; -//│ Cont$$4 = function Cont$$(res$0, pc) { +//│ Cont$handleBlock$stackHandler$$1 = function Cont$handleBlock$stackHandler$$(res$0, pc) { //│ let tmp; -//│ tmp = new Cont$10.class(pc); +//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); //│ return tmp(res$0) //│ }; -//│ Cont$$ctor4 = function Cont$$ctor(res$0) { +//│ Cont$handleBlock$stackHandler$$ctor1 = function Cont$handleBlock$stackHandler$$ctor(res$0) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$10.class(pc); +//│ tmp = new Cont$handleBlock$stackHandler$3.class(pc); //│ return tmp(res$0) //│ } //│ }; -//│ Cont$10 = function Cont$(pc1) { +//│ Cont$handleBlock$stackHandler$3 = function Cont$handleBlock$stackHandler$(pc1) { //│ return (res$01) => { -//│ return new Cont$.class(pc1)(res$01); +//│ return new Cont$handleBlock$stackHandler$.class(pc1)(res$01); //│ } //│ }; -//│ Cont$10.class = class Cont$8 extends globalThis.Predef.__Cont.class { +//│ Cont$handleBlock$stackHandler$3.class = class Cont$handleBlock$stackHandler$2 extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (res$0) => { //│ let tmp; -//│ tmp = super(null, null); +//│ tmp = super(null); //│ this.pc = pc; //│ this.res$0 = res$0; //│ return this; @@ -509,52 +336,33 @@ sum(10000) //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ if (this.res$0 instanceof globalThis.Predef.__Return.class) { -//│ return this.res$0 -//│ } -//│ this.pc = 4; -//│ continue contLoop; -//│ } else if (this.pc === 4) { //│ return this.res$0 //│ } //│ break; //│ } //│ } -//│ toString() { return "Cont$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ handleBlock$$capture3 = function handleBlock$$capture(stackHandler0$1) { -//│ return new handleBlock$$capture.class(stackHandler0$1); -//│ }; -//│ handleBlock$$capture3.class = class handleBlock$$capture2 { -//│ constructor(stackHandler0$) { -//│ this.stackHandler0$ = stackHandler0$; -//│ } -//│ toString() { return "handleBlock$$capture(" + globalThis.Predef.render(this.stackHandler0$) + ")"; } +//│ toString() { return "Cont$handleBlock$stackHandler$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ handleBlock$1 = function handleBlock$() { -//│ let res2, capture; -//│ capture = new handleBlock$$capture3(null); -//│ capture.stackHandler0$ = StackDelay$$1(capture); -//│ globalThis.Predef.__stackLimit = 1000; -//│ globalThis.Predef.__stackOffset = 0; -//│ globalThis.Predef.__stackDepth = 1; -//│ globalThis.Predef.__stackHandler = capture.stackHandler0$; +//│ let stackHandler, res2; +//│ stackHandler = new StackDelay$3(); +//│ runtime.stackLimit = 1000; +//│ runtime.stackOffset = 0; +//│ runtime.stackDepth = 1; +//│ runtime.stackHandler = stackHandler; //│ res2 = sum1(10000); -//│ if (res2 instanceof globalThis.Predef.__EffectSig.class) { -//│ res2.tail.next = Cont$$4(res2, 3); -//│ return globalThis.Predef.__handleBlockImpl(res2, capture.stackHandler0$) -//│ } -//│ if (res2 instanceof globalThis.Predef.__Return.class) { -//│ return res2 +//│ if (res2 instanceof runtime.EffectSig.class) { +//│ res2.contTrace.last.next = Cont$handleBlock$stackHandler$$1(res2, 3); +//│ return runtime.handleBlockImpl(res2, stackHandler) //│ } //│ return res2 //│ }; //│ res1 = handleBlock$1(); -//│ if (res1 instanceof this.Predef.__EffectSig.class) { +//│ if (res1 instanceof runtime.EffectSig.class) { //│ throw new this.Error("Unhandled effects"); //│ } -//│ this.Predef.__stackDepth = 0; -//│ this.Predef.__stackHandler = null; +//│ runtime.stackDepth = 0; +//│ runtime.stackHandler = null; //│ res1 //│ = 50005000 From e69fc929e081c5025c5c4d1ae535b25f0ec4c8da Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:24:18 +0800 Subject: [PATCH 114/127] Recompile runtime --- .../src/test/mlscript-compile/Runtime.mjs | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs index 85cc6777c..b8ca107ff 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs @@ -10,14 +10,18 @@ Runtime1 = class Runtime { }; this.Unit = new Unit$class; this.Unit.class = Unit$class; - this.FunctionContFrame = function FunctionContFrame(next1) { return new FunctionContFrame.class(next1); }; + this.FunctionContFrame = function FunctionContFrame(next1) { + return new FunctionContFrame.class(next1); + }; this.FunctionContFrame.class = class FunctionContFrame { constructor(next) { this.next = next; } toString() { return "FunctionContFrame(" + globalThis.Predef.render(this.next) + ")"; } }; - this.HandlerContFrame = function HandlerContFrame(next1, nextHandler1, handler1) { return new HandlerContFrame.class(next1, nextHandler1, handler1); }; + this.HandlerContFrame = function HandlerContFrame(next1, nextHandler1, handler1) { + return new HandlerContFrame.class(next1, nextHandler1, handler1); + }; this.HandlerContFrame.class = class HandlerContFrame { constructor(next, nextHandler, handler) { this.next = next; @@ -26,7 +30,9 @@ Runtime1 = class Runtime { } toString() { return "HandlerContFrame(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.nextHandler) + ", " + globalThis.Predef.render(this.handler) + ")"; } }; - this.ContTrace = function ContTrace(next1, last1, nextHandler1, lastHandler1, resumed1) { return new ContTrace.class(next1, last1, nextHandler1, lastHandler1, resumed1); }; + this.ContTrace = function ContTrace(next1, last1, nextHandler1, lastHandler1, resumed1) { + return new ContTrace.class(next1, last1, nextHandler1, lastHandler1, resumed1); + }; this.ContTrace.class = class ContTrace { constructor(next, last, nextHandler, lastHandler, resumed) { this.next = next; @@ -37,7 +43,9 @@ Runtime1 = class Runtime { } toString() { return "ContTrace(" + globalThis.Predef.render(this.next) + ", " + globalThis.Predef.render(this.last) + ", " + globalThis.Predef.render(this.nextHandler) + ", " + globalThis.Predef.render(this.lastHandler) + ", " + globalThis.Predef.render(this.resumed) + ")"; } }; - this.EffectSig = function EffectSig(contTrace1, handler1, handlerFun1) { return new EffectSig.class(contTrace1, handler1, handlerFun1); }; + this.EffectSig = function EffectSig(contTrace1, handler1, handlerFun1) { + return new EffectSig.class(contTrace1, handler1, handlerFun1); + }; this.EffectSig.class = class EffectSig { constructor(contTrace, handler, handlerFun) { this.contTrace = contTrace; @@ -46,7 +54,9 @@ Runtime1 = class Runtime { } toString() { return "EffectSig(" + globalThis.Predef.render(this.contTrace) + ", " + globalThis.Predef.render(this.handler) + ", " + globalThis.Predef.render(this.handlerFun) + ")"; } }; - this.Return = function Return(value1) { return new Return.class(value1); }; + this.Return = function Return(value1) { + return new Return.class(value1); + }; this.Return.class = class Return { constructor(value) { this.value = value; @@ -85,12 +95,12 @@ Runtime1 = class Runtime { throw globalThis.Error(tmp3); } static showFunctionContChain(cont, hl, vis, reps) { - let scrut, result, scrut1, scrut2, scrut3, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; + let scrut, result, scrut1, scrut2, scrut3, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, lambda; if (cont instanceof Runtime.FunctionContFrame.class) { tmp = cont.constructor.name + "(pc="; tmp1 = tmp + cont.pc; result = tmp1; - tmp2 = (m, marker) => { + lambda = function lambda(m, marker) { let scrut4, tmp12, tmp13; scrut4 = runtime.safeCall(m.has(cont)); if (scrut4 === true) { @@ -102,6 +112,7 @@ Runtime1 = class Runtime { return runtime.Unit } }; + tmp2 = lambda; tmp3 = runtime.safeCall(hl.forEach(tmp2)); scrut1 = runtime.safeCall(vis.has(cont)); if (scrut1 === true) { @@ -140,10 +151,10 @@ Runtime1 = class Runtime { } } static showHandlerContChain(cont1, hl1, vis1, reps1) { - let scrut, result, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + let scrut, result, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, lambda; if (cont1 instanceof Runtime.HandlerContFrame.class) { result = cont1.handler.constructor.name; - tmp = (m, marker) => { + lambda = function lambda(m, marker) { let scrut3, tmp8, tmp9; scrut3 = runtime.safeCall(m.has(cont1)); if (scrut3 === true) { @@ -155,6 +166,7 @@ Runtime1 = class Runtime { return runtime.Unit } }; + tmp = lambda; tmp1 = runtime.safeCall(hl1.forEach(tmp)); scrut1 = runtime.safeCall(vis1.has(cont1)); if (scrut1 === true) { From 2fea8eff634b8cc80bccb3520a38ab01c3d9cef9 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:31:47 +0800 Subject: [PATCH 115/127] update comment --- hkmc2/shared/src/test/mlscript/backlog/Lifter.mls | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 66c378cf1..95d12b6e1 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -23,7 +23,19 @@ f(2) // We use the optional `symbol` parameter of `Select` to detect references to the // BlockMemberSymbol. But when this symbol is not present, there is no way to properly -// detect it. What to do? +// detect it. +// +// The following could instead be lifted as: +// +// class B$(y)(a) with +// fun getB() = a.x + y +// class A(x) with +// fun B(y) = B$(y)(this) +// set B.class = B // so that `new a.B(n)` still works +// fun getA() = this.B(2).getB() +// A(1).getA() +// +// where B must be marked as final. :expect 3 class A(x) with class B(y) with From 100d2b4752011ab536908cbdb344d78b08ade90f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:32:41 +0800 Subject: [PATCH 116/127] update test --- .../test/mlscript/lifter/StackSafetyLift.mls | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 472b6668a..25d9ee0b2 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -187,25 +187,25 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; -//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { +//│ let sum1, res1, Cont$func$sum$StackSafetyLift$_mls_L184_4_57$1, handleBlock$1, Cont$handleBlock$stackHandler$3, StackDelay$3, lambda1, Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$ctor, Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$, Cont$handleBlock$stackHandler$$ctor1, Cont$handleBlock$stackHandler$$1, lambda$1; +//│ Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$ = function Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5, pc) { //│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L184_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { +//│ Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$ctor = function Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$ctor(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) { //│ return (pc) => { //│ let tmp; -//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class(pc); +//│ tmp = new Cont$func$sum$StackSafetyLift$_mls_L184_4_57$1.class(pc); //│ return tmp(n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) //│ } //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L213_4_57$(pc1) { +//│ Cont$func$sum$StackSafetyLift$_mls_L184_4_57$1 = function Cont$func$sum$StackSafetyLift$_mls_L184_4_57$(pc1) { //│ return (n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51) => { -//│ return new Cont$func$sum$StackSafetyLift$_mls_L213_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); +//│ return new Cont$func$sum$StackSafetyLift$_mls_L184_4_57$.class(pc1)(n$01, scrut$11, tmp$21, tmp$31, curDepth$41, stackDelayRes$51); //│ } //│ }; -//│ Cont$func$sum$StackSafetyLift$_mls_L213_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L213_4_57$ extends runtime.FunctionContFrame.class { +//│ Cont$func$sum$StackSafetyLift$_mls_L184_4_57$1.class = class Cont$func$sum$StackSafetyLift$_mls_L184_4_57$ extends runtime.FunctionContFrame.class { //│ constructor(pc) { //│ return (n$0, scrut$1, tmp$2, tmp$3, curDepth$4, stackDelayRes$5) => { //│ let tmp; @@ -255,14 +255,14 @@ sum(10000) //│ break; //│ } //│ } -//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L213_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } +//│ toString() { return "Cont$func$sum$StackSafetyLift$_mls_L184_4_57$(" + globalThis.Predef.render(this.pc) + ")"; } //│ }; //│ sum1 = function sum(n) { //│ let scrut, tmp, tmp1, curDepth, stackDelayRes; //│ curDepth = runtime.stackDepth; //│ stackDelayRes = runtime.checkDepth(); //│ if (stackDelayRes instanceof runtime.EffectSig.class) { -//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); +//│ stackDelayRes.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 0); //│ stackDelayRes.contTrace.last = stackDelayRes.contTrace.last.next; //│ return stackDelayRes //│ } @@ -274,7 +274,7 @@ sum(10000) //│ runtime.stackDepth = runtime.stackDepth + 1; //│ tmp1 = sum1(tmp); //│ if (tmp1 instanceof runtime.EffectSig.class) { -//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L213_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); +//│ tmp1.contTrace.last.next = Cont$func$sum$StackSafetyLift$_mls_L184_4_57$$(n, scrut, tmp, tmp1, curDepth, stackDelayRes, 1); //│ tmp1.contTrace.last = tmp1.contTrace.last.next; //│ return tmp1 //│ } From 09496e4fc477b374ecd9008fd43dd5a27104e7fa Mon Sep 17 00:00:00 2001 From: Mark Ng <55091936+CAG2Mark@users.noreply.github.com> Date: Tue, 25 Feb 2025 10:38:49 +0000 Subject: [PATCH 117/127] Update hkmc2/shared/src/test/mlscript/backlog/Lifter.mls Co-authored-by: Lionel Parreaux --- hkmc2/shared/src/test/mlscript/backlog/Lifter.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 95d12b6e1..11c23d872 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -53,7 +53,7 @@ fun hello() = //│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null // This is due to subclasses not calling `super` with the required locals. The analysis to -// determine which locals are required is in-place yet. +// determine which locals are required is not in place yet. :expect 2 fun test(x) = class A with From 9defc616feaa46dd1a1189a61bf74c9f392ede80 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:40:15 +0800 Subject: [PATCH 118/127] merge --- hkmc2/shared/src/test/mlscript/backlog/Lifter.mls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 95d12b6e1..706504fb8 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -53,7 +53,7 @@ fun hello() = //│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null // This is due to subclasses not calling `super` with the required locals. The analysis to -// determine which locals are required is in-place yet. +// determine which locals are required not is in-place yet. :expect 2 fun test(x) = class A with From 6f47c8b954cbbebe78e4774c655e21042d9b8aa1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 18:42:07 +0800 Subject: [PATCH 119/127] remove :sjs --- .../src/test/mlscript/handlers/Effects.mls | 245 ------------------ 1 file changed, 245 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index 02662aecc..1780608df 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -363,7 +363,6 @@ handle h = Eff with foo(h) //│ = 123 -:sjs :expect 123 fun foo(h) = h.perform() @@ -388,248 +387,4 @@ fun foo(h) = handle h = Eff with fun perform()(k) = k(()) foo(h) -//│ JS (unsanitized): -//│ let foo7, tmp20, handleBlock$20; -//│ foo7 = function foo(h) { -//│ let A10, A11, A12, A13, A14, a, scrut, b, scrut1, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, Cont$func$foo$Effects$_mls_L368_4_310$1; -//│ Cont$func$foo$Effects$_mls_L368_4_310$1 = function Cont$func$foo$Effects$_mls_L368_4_310$(pc1) { -//│ return new Cont$func$foo$Effects$_mls_L368_4_310$.class(pc1); -//│ }; -//│ Cont$func$foo$Effects$_mls_L368_4_310$1.class = class Cont$func$foo$Effects$_mls_L368_4_310$ extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ let tmp28; -//│ tmp28 = super(null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 0) { -//│ tmp21 = value$; -//│ } else if (this.pc === 2) { -//│ tmp22 = value$; -//│ } else if (this.pc === 1) { -//│ tmp22 = value$; -//│ } else if (this.pc === 4) { -//│ tmp23 = value$; -//│ } else if (this.pc === 3) { -//│ tmp23 = value$; -//│ } else if (this.pc === 5) { -//│ tmp24 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 0) { -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp22 = A10.f(); -//│ if (tmp22 instanceof runtime.EffectSig.class) { -//│ this.pc = 1; -//│ tmp22.contTrace.last.next = this; -//│ tmp22.contTrace.last = this; -//│ return tmp22 -//│ } -//│ this.pc = 1; -//│ continue contLoop; -//│ } else { -//│ tmp22 = A11.f(); -//│ if (tmp22 instanceof runtime.EffectSig.class) { -//│ this.pc = 2; -//│ tmp22.contTrace.last.next = this; -//│ tmp22.contTrace.last = this; -//│ return tmp22 -//│ } -//│ this.pc = 2; -//│ continue contLoop; -//│ } -//│ this.pc = 7; -//│ continue contLoop; -//│ } else if (this.pc === 7) { -//│ a = tmp22; -//│ scrut1 = false; -//│ if (scrut1 === true) { -//│ tmp23 = A12.f(); -//│ if (tmp23 instanceof runtime.EffectSig.class) { -//│ this.pc = 3; -//│ tmp23.contTrace.last.next = this; -//│ tmp23.contTrace.last = this; -//│ return tmp23 -//│ } -//│ this.pc = 3; -//│ continue contLoop; -//│ } else { -//│ tmp23 = A13.f(); -//│ if (tmp23 instanceof runtime.EffectSig.class) { -//│ this.pc = 4; -//│ tmp23.contTrace.last.next = this; -//│ tmp23.contTrace.last = this; -//│ return tmp23 -//│ } -//│ this.pc = 4; -//│ continue contLoop; -//│ } -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 2) { -//│ this.pc = 7; -//│ continue contLoop; -//│ } else if (this.pc === 1) { -//│ this.pc = 7; -//│ continue contLoop; -//│ } else if (this.pc === 6) { -//│ b = tmp23; -//│ tmp24 = A14(); -//│ if (tmp24 instanceof runtime.EffectSig.class) { -//│ this.pc = 5; -//│ tmp24.contTrace.last.next = this; -//│ tmp24.contTrace.last = this; -//│ return tmp24 -//│ } -//│ this.pc = 5; -//│ continue contLoop; -//│ } else if (this.pc === 4) { -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 3) { -//│ this.pc = 6; -//│ continue contLoop; -//│ } else if (this.pc === 5) { -//│ tmp25 = tmp24 * 100; -//│ tmp26 = a * 10; -//│ tmp27 = tmp25 + tmp26; -//│ return tmp27 + b -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$func$foo$Effects$_mls_L368_4_310$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ A14 = function A() { -//│ return 1 -//│ }; -//│ A10 = class A6 { -//│ static {} -//│ static f() { -//│ return 2 -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A11 = class A7 { -//│ static {} -//│ static f() { -//│ return 3 -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A12 = class A8 { -//│ static {} -//│ static f() { -//│ return 2 -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ A13 = class A9 { -//│ static {} -//│ static f() { -//│ return 3 -//│ } -//│ static toString() { return "A"; } -//│ }; -//│ tmp21 = runtime.safeCall(h.perform()); -//│ if (tmp21 instanceof runtime.EffectSig.class) { -//│ tmp21.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(0); -//│ tmp21.contTrace.last = tmp21.contTrace.last.next; -//│ return tmp21 -//│ } -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp22 = A10.f(); -//│ if (tmp22 instanceof runtime.EffectSig.class) { -//│ tmp22.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(1); -//│ tmp22.contTrace.last = tmp22.contTrace.last.next; -//│ return tmp22 -//│ } -//│ } else { -//│ tmp22 = A11.f(); -//│ if (tmp22 instanceof runtime.EffectSig.class) { -//│ tmp22.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(2); -//│ tmp22.contTrace.last = tmp22.contTrace.last.next; -//│ return tmp22 -//│ } -//│ } -//│ a = tmp22; -//│ scrut1 = false; -//│ if (scrut1 === true) { -//│ tmp23 = A12.f(); -//│ if (tmp23 instanceof runtime.EffectSig.class) { -//│ tmp23.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(3); -//│ tmp23.contTrace.last = tmp23.contTrace.last.next; -//│ return tmp23 -//│ } -//│ } else { -//│ tmp23 = A13.f(); -//│ if (tmp23 instanceof runtime.EffectSig.class) { -//│ tmp23.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(4); -//│ tmp23.contTrace.last = tmp23.contTrace.last.next; -//│ return tmp23 -//│ } -//│ } -//│ b = tmp23; -//│ tmp24 = A14(); -//│ if (tmp24 instanceof runtime.EffectSig.class) { -//│ tmp24.contTrace.last.next = new Cont$func$foo$Effects$_mls_L368_4_310$1.class(5); -//│ tmp24.contTrace.last = tmp24.contTrace.last.next; -//│ return tmp24 -//│ } -//│ tmp25 = tmp24 * 100; -//│ tmp26 = a * 10; -//│ tmp27 = tmp25 + tmp26; -//│ return tmp27 + b -//│ }; -//│ handleBlock$20 = function handleBlock$() { -//│ let h, res3, Cont$handleBlock$h$21, Handler$h$22; -//│ Handler$h$22 = class Handler$h$21 extends Eff1 { -//│ constructor() { -//│ let tmp21; -//│ tmp21 = super(); -//│ } -//│ perform() { -//│ return runtime.mkEffect(this, (k) => { -//│ return runtime.safeCall(k(runtime.Unit)) -//│ }) -//│ } -//│ toString() { return "Handler$h$"; } -//│ }; -//│ h = new Handler$h$22(); -//│ Cont$handleBlock$h$21 = function Cont$handleBlock$h$(pc1) { -//│ return new Cont$handleBlock$h$.class(pc1); -//│ }; -//│ Cont$handleBlock$h$21.class = class Cont$handleBlock$h$20 extends runtime.FunctionContFrame.class { -//│ constructor(pc) { -//│ let tmp21; -//│ tmp21 = super(null); -//│ this.pc = pc; -//│ } -//│ resume(value$) { -//│ if (this.pc === 8) { -//│ res3 = value$; -//│ } -//│ contLoop: while (true) { -//│ if (this.pc === 8) { -//│ return res3 -//│ } -//│ break; -//│ } -//│ } -//│ toString() { return "Cont$handleBlock$h$(" + globalThis.Predef.render(this.pc) + ")"; } -//│ }; -//│ res3 = foo7(h); -//│ if (res3 instanceof runtime.EffectSig.class) { -//│ res3.contTrace.last.next = Cont$handleBlock$h$21(8); -//│ return runtime.handleBlockImpl(res3, h) -//│ } -//│ return res3 -//│ }; -//│ tmp20 = handleBlock$20(); -//│ if (tmp20 instanceof runtime.EffectSig.class) { -//│ throw new this.Error("Unhandled effects"); -//│ } -//│ tmp20 //│ = 123 From 9fb5f7b0c388d0ee67706f0a61d2d5a95c58522e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 19:15:48 +0800 Subject: [PATCH 120/127] fix lambda names --- .../scala/hkmc2/codegen/LambdaRewriter.scala | 2 +- .../scala/hkmc2/codegen/js/JSBuilder.scala | 7 ++-- .../main/scala/hkmc2/semantics/Symbol.scala | 2 +- .../mlscript/basics/BadMemberProjections.mls | 14 ++++---- .../mlscript/basics/MemberProjections.mls | 36 ++++++++++--------- .../src/test/mlscript/basics/OpBlocks.mls | 2 +- .../mlscript/basics/PureTermStatements.mls | 2 +- .../src/test/mlscript/bbml/bbCodeGen.mls | 16 ++++----- .../src/test/mlscript/bbml/bbGetters.mls | 6 ++-- .../src/test/mlscript/codegen/BadNew.mls | 2 +- .../src/test/mlscript/codegen/BuiltinOps.mls | 16 ++++----- .../test/mlscript/codegen/CaseShorthand.mls | 36 +++++++++++-------- .../test/mlscript/codegen/ClassMatching.mls | 6 ++-- .../test/mlscript/codegen/DelayedLetInit.mls | 4 +-- hkmc2/shared/src/test/mlscript/codegen/Do.mls | 2 +- .../test/mlscript/codegen/FieldSymbols.mls | 2 +- .../src/test/mlscript/codegen/Getters.mls | 4 +-- .../src/test/mlscript/codegen/Hygiene.mls | 6 ++-- .../src/test/mlscript/codegen/IfThenElse.mls | 26 +++++++------- .../src/test/mlscript/codegen/ImportMLs.mls | 4 +-- .../src/test/mlscript/codegen/ImportedOps.mls | 2 +- .../test/mlscript/codegen/InlineLambdas.mls | 16 ++++----- .../src/test/mlscript/codegen/Lambdas.mls | 8 ++--- .../src/test/mlscript/codegen/OptMatch.mls | 10 +++--- .../src/test/mlscript/codegen/PartialApps.mls | 24 ++++++------- .../src/test/mlscript/codegen/ReboundLet.mls | 2 +- .../test/mlscript/codegen/SanityChecks.mls | 10 +++--- .../src/test/mlscript/codegen/SetIn.mls | 8 ++--- .../src/test/mlscript/codegen/Spreads.mls | 2 +- .../src/test/mlscript/codegen/While.mls | 16 ++++----- .../mlscript/handlers/ReturnInHandler.mls | 2 +- .../test/mlscript/handlers/ZCombinator.mls | 4 +-- .../src/test/mlscript/lifter/ClassInFun.mls | 4 +-- .../src/test/mlscript/lifter/FunInFun.mls | 8 ++--- .../test/mlscript/lifter/StackSafetyLift.mls | 8 ++--- .../src/test/mlscript/nofib/cryptarithm2.mls | 2 +- hkmc2/shared/src/test/mlscript/nofib/cse.mls | 2 +- .../shared/src/test/mlscript/nofib/eliza.mls | 2 +- .../shared/src/test/mlscript/nofib/lambda.mls | 4 +-- hkmc2/shared/src/test/mlscript/nofib/life.mls | 2 +- .../src/test/mlscript/parser/PrefixOps.mls | 10 +++--- .../src/test/mlscript/std/Rendering.mls | 2 +- .../syntax/annotations/Declarations.mls | 4 +-- .../ucs/normalization/SimplePairMatches.mls | 6 ++-- 44 files changed, 183 insertions(+), 170 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala index 334881099..86f4ad042 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -14,7 +14,7 @@ object LambdaRewriter: val lambdaRewriter = new BlockDataTransformer(SymbolSubst()): override def applyValue(v: Value): Value = v match case lam: Value.Lam => - val sym = BlockMemberSymbol("lambda", Nil) + val sym = BlockMemberSymbol("lambda", Nil, true) lambdasList ::= (sym -> super.applyLam(lam)) Value.Ref(sym) case _ => super.applyValue(v) 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 678ea45f8..2db1880c8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -181,9 +181,12 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: case FunDefn(own, sym, ps :: pss, bod) => val result = pss.foldRight(bod): case (ps, block) => - Return(Lam(ps, block), false) + Return(Lam(ps, block), false) val (params, bodyDoc) = setupFunction(some(sym.nme), ps, result) - doc"${getVar(sym)} = function ${sym.nme}($params) ${ braced(bodyDoc) };" + if sym.isTmp then + doc"${getVar(sym)} = (undefined, function ($params) ${ braced(bodyDoc) } );" + else + doc"${getVar(sym)} = function ${sym.nme}($params) ${ braced(bodyDoc) };" case ClsLikeDefn(ownr, isym, sym, kind, paramsOpt, auxParams, par, mtds, privFlds, _pubFlds, preCtor, ctor) => // * Note: `_pubFlds` is not used because in JS, fields are not declared val clsParams = paramsOpt.fold(Nil)(_.paramSyms) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index 7ba794f36..d34f01863 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -131,7 +131,7 @@ class BuiltinSymbol /** This is the outside-facing symbol associated to a possibly-overloaded * definition living in a block – e.g., a module or class. */ -class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree])(using State) +class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree], val isTmp: Bool = false)(using State) extends MemberSymbol[Definition]: def toLoc: Option[Loc] = Loc(trees) diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index cc049f619..2607dc965 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -11,11 +11,11 @@ //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS (unsanitized): //│ let lambda; -//│ lambda = function lambda(self, ...args) { +//│ lambda = (undefined, function (self, ...args) { //│ return runtime.safeCall(self.x(...args)) -//│ }; +//│ } ); //│ lambda -//│ = [function lambda] +//│ = [function] :ssjs :e @@ -27,12 +27,12 @@ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS: -//│ lambda1 = function lambda(...args1) { +//│ lambda1 = (undefined, function (...args1) { //│ globalThis.Predef.checkArgs("lambda", 1, false, args1.length); //│ let self = args1[0]; //│ let args = globalThis.Predef.tupleSlice(args1, 1, 0); //│ return runtime.safeCall(self.x(...args)) -//│ }; +//│ } ); //│ block$res2 = runtime.checkCall(lambda1()); //│ undefined //│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 @@ -56,7 +56,7 @@ let x = 1 //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function lambda] +//│ = [function] :e "A" ::x @@ -65,6 +65,6 @@ let x = 1 //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function lambda] +//│ = [function] diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index aba8e2a58..ae97dbff2 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -46,8 +46,12 @@ M.Foo:: n(foo, 2) :sjs let m = M.Foo::m //│ JS (unsanitized): -//│ let m, lambda5; lambda5 = function lambda(self, ...args) { return self.m(...args) }; m = lambda5; -//│ m = [function lambda] +//│ let m, lambda5; +//│ lambda5 = (undefined, function (self, ...args) { +//│ return self.m(...args) +//│ } ); +//│ m = lambda5; +//│ m = [function] m(foo) //│ = 124 @@ -76,10 +80,10 @@ do :e foo.Foo#m() //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.77: foo.Foo#m() +//│ ║ l.81: foo.Foo#m() //│ ╙── ^^^^ //│ ╔══[ERROR] Identifier `Foo` does not name a known class symbol. -//│ ║ l.77: foo.Foo#m() +//│ ║ l.81: foo.Foo#m() //│ ╙── ^^^^ //│ = 124 @@ -87,7 +91,7 @@ foo.Foo#m() :re foo.M.Foo::m() //│ ╔══[ERROR] Selection is not a known class. -//│ ║ l.88: foo.M.Foo::m() +//│ ║ l.92: foo.M.Foo::m() //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -97,41 +101,41 @@ foo.M.Foo::m() :re foo.M.Foo#m() //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.98: foo.M.Foo#m() -//│ ╙── ^^^^ +//│ ║ l.102: foo.M.Foo#m() +//│ ╙── ^^^^ //│ ╔══[ERROR] Identifier `Foo` does not name a known class symbol. -//│ ║ l.98: foo.M.Foo#m() -//│ ╙── ^^^^ +//│ ║ l.102: foo.M.Foo#m() +//│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'M' yielded 'undefined' :e Foo::m //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.108: Foo::m +//│ ║ l.112: Foo::m //│ ╙── ^^^ //│ ╔══[ERROR] Identifier is not a known class. -//│ ║ l.108: Foo::m +//│ ║ l.112: Foo::m //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ = [function lambda] +//│ = [function] :e :sjs Foo::n(foo, 2) //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.121: Foo::n(foo, 2) +//│ ║ l.125: Foo::n(foo, 2) //│ ╙── ^^^ //│ ╔══[ERROR] Identifier is not a known class. -//│ ║ l.121: Foo::n(foo, 2) +//│ ║ l.125: Foo::n(foo, 2) //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS (unsanitized): //│ let lambda10; -//│ lambda10 = function lambda(self, ...args) { +//│ lambda10 = (undefined, function (self, ...args) { //│ return runtime.safeCall(self.n(...args)) -//│ }; +//│ } ); //│ lambda10(foo, 2) //│ = 125 diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 924ccbe6b..4f0c59472 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -31,7 +31,7 @@ let f = x => x * 2 -//│ f = [function lambda] +//│ f = [function] 2 + 1 diff --git a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls index f3f4dea66..b96f96c91 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PureTermStatements.mls @@ -21,7 +21,7 @@ case x then x //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.19: case x then x //│ ╙── ^ -//│ = [function lambda] +//│ = [function] :w diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index bbeadd163..c5ee35a40 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -53,8 +53,8 @@ false :sjs (x => x): [T] -> T -> T //│ JS (unsanitized): -//│ let lambda; lambda = function lambda(x1) { return x1 }; lambda -//│ = [function lambda] +//│ let lambda; lambda = (undefined, function (x1) { return x1 } ); lambda +//│ = [function] //│ Type: ['T] -> ('T) ->{⊥} 'T @@ -151,7 +151,7 @@ fun pow(x) = case //│ let pow; //│ pow = function pow(x1) { //│ let lambda1; -//│ lambda1 = function lambda(caseScrut) { +//│ lambda1 = (undefined, function (caseScrut) { //│ let n, tmp2, tmp3, tmp4; //│ if (caseScrut === 0) { //│ return 1 @@ -162,7 +162,7 @@ fun pow(x) = case //│ tmp4 = runtime.safeCall(tmp2(tmp3)); //│ return x1 * tmp4 //│ } -//│ }; +//│ } ); //│ return lambda1 //│ }; //│ Type: ⊤ @@ -176,7 +176,7 @@ fun not = case //│ let not; //│ not = function not() { //│ let lambda1; -//│ lambda1 = function lambda(caseScrut) { +//│ lambda1 = (undefined, function (caseScrut) { //│ if (caseScrut === true) { //│ return false //│ } else if (caseScrut === false) { @@ -184,7 +184,7 @@ fun not = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ return lambda1 //│ }; //│ Type: ⊤ @@ -207,7 +207,7 @@ fun fact = case //│ let fact; //│ fact = function fact() { //│ let lambda1; -//│ lambda1 = function lambda(caseScrut) { +//│ lambda1 = (undefined, function (caseScrut) { //│ let n, tmp3, tmp4, tmp5; //│ if (caseScrut === 0) { //│ return 1 @@ -218,7 +218,7 @@ fun fact = case //│ tmp5 = tmp3(tmp4); //│ return n * tmp5 //│ } -//│ }; +//│ } ); //│ return lambda1 //│ }; //│ Type: ⊤ diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls index 55b5ba80f..cdfdb63e3 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls @@ -59,7 +59,7 @@ test2 test2() //│ JS (unsanitized): //│ test21() -//│ = [function lambda] +//│ = [function] //│ Type: Int -> Int :ssjs @@ -86,14 +86,14 @@ fun test2() = //│ let funny, tmp1; //│ funny = function funny() { //│ let lambda; -//│ lambda = function lambda(caseScrut) { +//│ lambda = (undefined, function (caseScrut) { //│ let n, tmp2, tmp3, tmp4; //│ n = caseScrut; //│ tmp2 = funny(); //│ tmp3 = n - 1; //│ tmp4 = tmp2(tmp3); //│ return tmp4 + 1 -//│ }; +//│ } ); //│ return lambda //│ }; //│ tmp1 = funny(); diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index e151bc319..03d36f92c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -14,7 +14,7 @@ new 2 :sjs new 2 + 2 //│ JS (unsanitized): -//│ let lambda; lambda = function lambda(arg1, arg2) { return arg1 + arg2 }; new lambda(2, 2) +//│ let lambda; lambda = (undefined, function (arg1, arg2) { return arg1 + arg2 } ); new lambda(2, 2) //│ = [object Object] :re diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls index ca6dfb914..6d2af5a80 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls @@ -20,7 +20,7 @@ //│ = 2 + -//│ = [function lambda] +//│ = [function] // * A bit confusing... but at least there's a warning! :w @@ -42,7 +42,7 @@ //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' (+) -//│ = [function lambda] +//│ = [function] (+)(2, 3) //│ = 5 @@ -59,7 +59,7 @@ //│ ═══[RUNTIME ERROR] Error: Cannot call non-unary builtin symbol '*' id(+) -//│ = [function lambda] +//│ = [function] id(+)(1, 2) //│ = 3 @@ -75,9 +75,9 @@ id(+)(1, 2) id(+)(1) //│ JS (unsanitized): //│ let tmp1, lambda4; -//│ lambda4 = function lambda(arg1, arg2) { +//│ lambda4 = (undefined, function (arg1, arg2) { //│ return arg1 + arg2 -//│ }; +//│ } ); //│ tmp1 = Predef.id(lambda4); //│ runtime.safeCall(tmp1(1)) //│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 @@ -96,9 +96,9 @@ fun (+) lol(a, b) = [a, b] id(~)(2) //│ JS (unsanitized): //│ let tmp2, lambda5; -//│ lambda5 = function lambda(arg) { +//│ lambda5 = (undefined, function (arg) { //│ return ~ arg -//│ }; +//│ } ); //│ tmp2 = Predef.id(lambda5); //│ runtime.safeCall(tmp2(2)) //│ = -3 @@ -108,7 +108,7 @@ id(~)(2) typeof -//│ = [function lambda] +//│ = [function] typeof(1) //│ = "number" diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index f93a3a2e6..cdc4c9b68 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -2,42 +2,48 @@ case x then x -//│ = [function lambda] +//│ = [function] :sjs case { x then x } //│ JS (unsanitized): -//│ let lambda1; lambda1 = function lambda(caseScrut) { let x; x = caseScrut; return x }; lambda1 -//│ = [function lambda] +//│ let lambda1; +//│ lambda1 = (undefined, function (caseScrut) { +//│ let x; +//│ x = caseScrut; +//│ return x +//│ } ); +//│ lambda1 +//│ = [function] :sjs x => if x is 0 then true //│ JS (unsanitized): //│ let lambda2; -//│ lambda2 = function lambda(x) { +//│ lambda2 = (undefined, function (x) { //│ if (x === 0) { //│ return true //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ lambda2 -//│ = [function lambda] +//│ = [function] :sjs case 0 then true //│ JS (unsanitized): //│ let lambda3; -//│ lambda3 = function lambda(caseScrut) { +//│ lambda3 = (undefined, function (caseScrut) { //│ if (caseScrut === 0) { //│ return true //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ lambda3 -//│ = [function lambda] +//│ = [function] (case x then x) of 1 //│ = 1 @@ -64,15 +70,15 @@ case _ then false //│ JS (unsanitized): //│ let lambda10; -//│ lambda10 = function lambda(caseScrut) { +//│ lambda10 = (undefined, function (caseScrut) { //│ if (caseScrut === 0) { //│ return true //│ } else { //│ return false //│ } -//│ }; +//│ } ); //│ lambda10 -//│ = [function lambda] +//│ = [function] class Some(value) module None @@ -83,7 +89,7 @@ val isDefined = case None then false //│ JS (unsanitized): //│ let isDefined, tmp5, lambda11; -//│ lambda11 = function lambda(caseScrut) { +//│ lambda11 = (undefined, function (caseScrut) { //│ if (caseScrut instanceof Some1.class) { //│ return true //│ } else if (caseScrut instanceof None1) { @@ -91,8 +97,8 @@ val isDefined = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ tmp5 = lambda11; //│ isDefined = tmp5; -//│ isDefined = [function lambda] +//│ isDefined = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index e6c1359ca..409e22227 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -58,7 +58,7 @@ if s is x => if x is Some(x) then x //│ JS (unsanitized): //│ let lambda; -//│ lambda = function lambda(x3) { +//│ lambda = (undefined, function (x3) { //│ let param04, x4; //│ if (x3 instanceof Some1.class) { //│ param04 = x3.value; @@ -67,9 +67,9 @@ x => if x is Some(x) then x //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ lambda -//│ = [function lambda] +//│ = [function] class C(a) diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index 484072966..8a876b707 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -55,8 +55,8 @@ f let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f1, lambda; lambda = function lambda(x1) { return x1 + 1 }; f1 = lambda; -//│ f = [function lambda] +//│ let f1, lambda; lambda = (undefined, function (x1) { return x1 + 1 } ); f1 = lambda; +//│ f = [function] f(1) //│ JS (unsanitized): diff --git a/hkmc2/shared/src/test/mlscript/codegen/Do.mls b/hkmc2/shared/src/test/mlscript/codegen/Do.mls index 976fa4df8..b3b77bb19 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Do.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Do.mls @@ -44,7 +44,7 @@ val f = case //│ > let res = "other" //│ > let $doTemp = (member:Predef#666.)print‹member:print›(res#666) //│ > else res#666 -//│ f = [function lambda] +//│ f = [function] f(0) //│ = "null" diff --git a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls index 312b4ee82..43041e965 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls @@ -134,7 +134,7 @@ case //│ rest = Return: \ //│ res = Lit of UnitLit of false //│ implct = true -//│ = [function lambda] +//│ = [function] // TODO support: diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index 9f8eef33e..d01b5f6d7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -163,14 +163,14 @@ fun baz() = //│ z = function z() { //│ return 2 //│ }; -//│ lambda = function lambda(x, y) { +//│ lambda = (undefined, function (x, y) { //│ let tmp1, tmp2, tmp3, tmp4; //│ tmp1 = x + y; //│ tmp2 = w(); //│ tmp3 = tmp1 + tmp2; //│ tmp4 = z(); //│ return tmp3 + tmp4 -//│ }; +//│ } ); //│ return lambda //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 3aa46bece..fce701ac8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -56,14 +56,14 @@ f() //│ JS (unsanitized): //│ let x, f, x1, lambda; //│ x = 1; -//│ lambda = function lambda() { +//│ lambda = (undefined, function () { //│ return x -//│ }; +//│ } ); //│ f = lambda; //│ x1 = 2; //│ runtime.safeCall(f()) //│ = 1 -//│ f = [function lambda] +//│ f = [function] //│ x = 2 diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index 08271033b..779694f3b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -10,15 +10,15 @@ if true then 1 else 0 let f = x => if x then print("ok") else print("ko") //│ JS (unsanitized): //│ let f, lambda; -//│ lambda = function lambda(x) { +//│ lambda = (undefined, function (x) { //│ if (x === true) { //│ return Predef.print("ok") //│ } else { //│ return Predef.print("ko") //│ } -//│ }; +//│ } ); //│ f = lambda; -//│ f = [function lambda] +//│ f = [function] f(true) //│ > ok @@ -31,7 +31,7 @@ f(false) let f = x => print((if x then "ok" else "ko") + "!") //│ JS (unsanitized): //│ let f1, tmp, lambda1; -//│ lambda1 = function lambda(x) { +//│ lambda1 = (undefined, function (x) { //│ let tmp1, tmp2; //│ if (x === true) { //│ tmp1 = "ok"; @@ -40,16 +40,16 @@ let f = x => print((if x then "ok" else "ko") + "!") //│ } //│ tmp2 = tmp1 + "!"; //│ return Predef.print(tmp2) -//│ }; +//│ } ); //│ tmp = lambda1; //│ f1 = tmp; -//│ f = [function lambda] +//│ f = [function] :sjs let f = x => print((if x and x then "ok" else "ko") + "!") //│ JS (unsanitized): //│ let f2, tmp1, lambda2; -//│ lambda2 = function lambda(x) { +//│ lambda2 = (undefined, function (x) { //│ let tmp2, tmp3; //│ if (x === true) { //│ tmp2 = "ok"; @@ -58,10 +58,10 @@ let f = x => print((if x and x then "ok" else "ko") + "!") //│ } //│ tmp3 = tmp2 + "!"; //│ return Predef.print(tmp3) -//│ }; +//│ } ); //│ tmp1 = lambda2; //│ f2 = tmp1; -//│ f = [function lambda] +//│ f = [function] // --- TODO: What we want --- // this.f = (x) => { // let tmp, tmp1, flag; @@ -91,18 +91,18 @@ f(false) x => if x > 0 then print("Hi") else print("Bye") -//│ = [function lambda] +//│ = [function] x => print(if true then "Hi" else "Bye") -//│ = [function lambda] +//│ = [function] x => print(if x + 1 > 0 then "Hi" else "Bye") -//│ = [function lambda] +//│ = [function] x => let str = (+)(if x + 1 > 0 then "Hello" else "Bye", "World") print(str) -//│ = [function lambda] +//│ = [function] fun f(x, y) = diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls index 3f726617c..9f1f40023 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportMLs.mls @@ -28,10 +28,10 @@ None isDefined() //│ = false case Some(x) then x -//│ = [function lambda] +//│ = [function] case { Some(x) then x } -//│ = [function lambda] +//│ = [function] Some(1) isDefined() //│ = true diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index 0b9b570de..a88049acc 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -24,7 +24,7 @@ let name = "_" //│ name = "_" let display(balance) = balance -//│ display = [function lambda] +//│ display = [function] "b" ~ display("-") //│ = "b-" diff --git a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls index 0cb831a19..3e4308ff9 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls @@ -4,14 +4,14 @@ (x => x + 1 + 1 + 1 + 1 + 1)(1) //│ JS (unsanitized): //│ let lambda; -//│ lambda = function lambda(x) { +//│ lambda = (undefined, function (x) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = x + 1; //│ tmp1 = tmp + 1; //│ tmp2 = tmp1 + 1; //│ tmp3 = tmp2 + 1; //│ return tmp3 + 1 -//│ }; +//│ } ); //│ lambda(1) //│ = 6 @@ -19,7 +19,7 @@ (x => x + 1 + 1 + 1 + 1 + 1 + 1)(1) //│ JS (unsanitized): //│ let tmp, lambda1; -//│ lambda1 = function lambda(x) { +//│ lambda1 = (undefined, function (x) { //│ let tmp1, tmp2, tmp3, tmp4, tmp5; //│ tmp1 = x + 1; //│ tmp2 = tmp1 + 1; @@ -27,7 +27,7 @@ //│ tmp4 = tmp3 + 1; //│ tmp5 = tmp4 + 1; //│ return tmp5 + 1 -//│ }; +//│ } ); //│ tmp = lambda1; //│ tmp(1) //│ = 7 @@ -35,13 +35,13 @@ :sjs (x => x) + 1 //│ JS (unsanitized): -//│ let lambda2; lambda2 = function lambda(x) { return x }; lambda2 + 1 -//│ = "function lambda(...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }1" +//│ let lambda2; lambda2 = (undefined, function (x) { return x } ); lambda2 + 1 +//│ = "function (...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }1" :sjs 1 + (x => x) //│ JS (unsanitized): -//│ let lambda3; lambda3 = function lambda(x) { return x }; 1 + lambda3 -//│ = "1function lambda(...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }" +//│ let lambda3; lambda3 = (undefined, function (x) { return x } ); 1 + lambda3 +//│ = "1function (...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls index e25c32269..76b38d69c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls @@ -8,14 +8,14 @@ x => let y = x y //│ JS (unsanitized): -//│ let lambda; lambda = function lambda(x) { let y; y = x; return y }; lambda -//│ = [function lambda] +//│ let lambda; lambda = (undefined, function (x) { let y; y = x; return y } ); lambda +//│ = [function] :todo (acc, _) => acc //│ JS (unsanitized): -//│ let lambda1; lambda1 = function lambda(acc, _) { return acc }; lambda1 -//│ = [function lambda] +//│ let lambda1; lambda1 = (undefined, function (acc, _) { return acc } ); lambda1 +//│ = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index 47470b5ef..3ac603997 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -34,7 +34,7 @@ val isDefined = case None then false //│ JS (unsanitized): //│ let isDefined1, tmp1, lambda; -//│ lambda = function lambda(caseScrut) { +//│ lambda = (undefined, function (caseScrut) { //│ let param0; //│ if (caseScrut instanceof Some1.class) { //│ param0 = caseScrut.value; @@ -44,10 +44,10 @@ val isDefined = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ tmp1 = lambda; //│ isDefined1 = tmp1; -//│ isDefined = [function lambda] +//│ isDefined = [function] isDefined(Some(1)) //│ = true @@ -59,7 +59,7 @@ isDefined(None) val isDefined = x => if x is Some(_) then true None then false -//│ isDefined = [function lambda] +//│ isDefined = [function] isDefined(Some(1)) //│ = true @@ -74,7 +74,7 @@ module Foo with val isOther = x => if x is Foo.Other(_) then true None then false -//│ isOther = [function lambda] +//│ isOther = [function] fun keepIfGreaterThan(x, y) = diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 59abac5cf..3e9094094 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -4,7 +4,7 @@ fun foo(x, y, z) = [x, y, z] let f = foo(1, _, 3) -//│ f = [function lambda] +//│ f = [function] f(2) //│ = [1, 2, 3] @@ -12,14 +12,14 @@ f(2) :sjs let f = foo(1, _, _) //│ JS (unsanitized): -//│ let f1, lambda1; lambda1 = function lambda(_, _1) { return foo(1, _, _1) }; f1 = lambda1; -//│ f = [function lambda] +//│ let f1, lambda1; lambda1 = (undefined, function (_, _1) { return foo(1, _, _1) } ); f1 = lambda1; +//│ f = [function] f(2, 3) //│ = [1, 2, 3] let g = f(_, 3) -//│ g = [function lambda] +//│ g = [function] g(2) //│ = [1, 2, 3] @@ -45,14 +45,14 @@ foo(..._) let h = _ - 2 -//│ h = [function lambda] +//│ h = [function] h(1) //│ = -1 let i = _(0, 1, _) -//│ i = [function lambda] +//│ i = [function] i((x, y, z) => x + y + z, 2) //│ = 3 @@ -97,12 +97,12 @@ let j = _.x(123) :todo let j = _.x(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function lambda] +//│ j = [function] :todo // really support this? let j = _.x.y(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function lambda] +//│ j = [function] class C(a, b, c) @@ -120,7 +120,7 @@ class C(a, b, c) _ - _ of 1, 2 -//│ = [function lambda] +//│ = [function] (_ - _) of 1, 2 //│ = -1 @@ -151,16 +151,16 @@ _ - 2 <| 1 :sjs 1 . _ - 2 //│ JS (unsanitized): -//│ let lambda30; lambda30 = function lambda(_) { return Predef.passTo(1, _) }; lambda30 - 2 +//│ let lambda30; lambda30 = (undefined, function (_) { return Predef.passTo(1, _) } ); lambda30 - 2 //│ = NaN :sjs 1 . (_ - 2)() //│ JS (unsanitized): //│ let tmp7, lambda31; -//│ lambda31 = function lambda(_) { +//│ lambda31 = (undefined, function (_) { //│ return _ - 2 -//│ }; +//│ } ); //│ tmp7 = Predef.passTo(1, lambda31); //│ runtime.safeCall(tmp7()) //│ = -1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls index 2ec569b4a..03c498c27 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls @@ -15,7 +15,7 @@ foo() let x = 1 let f = () => x -//│ f = [function lambda] +//│ f = [function] //│ x = 1 f() diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index 2e310da61..2348144e8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -62,9 +62,9 @@ f(3)(4) :noSanityCheck let f = (x, y) => x + y in f(2) //│ JS: -//│ lambda = function lambda(x, y) { +//│ lambda = (undefined, function (x, y) { //│ return x + y -//│ }; +//│ } ); //│ f3 = lambda; //│ block$res5 = runtime.safeCall(f3(2)); //│ undefined @@ -75,17 +75,17 @@ let f = (x, y) => x + y in f(2) let f = (x, y) => x + y f(2) //│ JS: -//│ lambda1 = function lambda(...args) { +//│ lambda1 = (undefined, function (...args) { //│ globalThis.Predef.checkArgs("lambda", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y -//│ }; +//│ } ); //│ f4 = lambda1; //│ block$res6 = runtime.safeCall(f4(2)); //│ undefined //│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 -//│ f = [function lambda] +//│ f = [function] :expect NaN diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 2cd10bf09..206dae241 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -80,9 +80,9 @@ example() //│ example2 = function example() { //│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, lambda; //│ x2 = 0; -//│ lambda = function lambda() { +//│ lambda = (undefined, function () { //│ return x2 -//│ }; +//│ } ); //│ get_x = lambda; //│ old1 = x2; //│ try { @@ -121,9 +121,9 @@ example() //│ example3 = function example() { //│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, lambda; //│ x2 = 0; -//│ lambda = function lambda() { +//│ lambda = (undefined, function () { //│ return x2 -//│ }; +//│ } ); //│ get_x = lambda; //│ old1 = x2; //│ try { diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index 3f2157499..70167d60a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -28,7 +28,7 @@ foo(...a) let f = (...xs) => xs -//│ f = [function lambda] +//│ f = [function] f(a) //│ = [[1, 2, 3]] diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index 15b8cd57f..c842e3d01 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -5,7 +5,7 @@ () => (while true then 0) //│ JS (unsanitized): //│ let lambda; -//│ lambda = function lambda() { +//│ lambda = (undefined, function () { //│ let scrut, tmp; //│ tmp1: while (true) { //│ scrut = true; @@ -18,13 +18,13 @@ //│ break; //│ } //│ return tmp -//│ }; +//│ } ); //│ lambda -//│ = [function lambda] +//│ = [function] () => while true then 0 -//│ = [function lambda] +//│ = [function] let x = true @@ -106,7 +106,7 @@ while i < 10 do set i += 1 //│ JS (unsanitized): //│ let lambda2; -//│ lambda2 = function lambda() { +//│ lambda2 = (undefined, function () { //│ let i2, scrut3, tmp19, tmp20; //│ tmp21: while (true) { //│ i2 = 0; @@ -122,9 +122,9 @@ while //│ break; //│ } //│ return tmp20 -//│ }; +//│ } ); //│ lambda2 -//│ = [function lambda] +//│ = [function] let i = 0 in @@ -252,7 +252,7 @@ while false do () => while true then 0 -//│ = [function lambda] +//│ = [function] :fixme while print("Hello World"); false diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index 0113000d0..fdde5dc1a 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -35,7 +35,7 @@ let l = () => fun foo()(r) = r(()) return 3 4 -//│ l = [function lambda] +//│ l = [function] l() //│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls index 28e8305c2..2da532a49 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ZCombinator.mls @@ -10,7 +10,7 @@ fun mkrec(g) = let fact = mkrec of self => x => if x == 0 then 1 else self(x - 1) * x -//│ fact = [function lambda] +//│ fact = [function] fact(3) //│ = 6 @@ -31,7 +31,7 @@ fun mkrec(g) = let fact = mkrec of self => x => if x == 0 then 1 else self(x - 1) * x -//│ fact = [function lambda] +//│ fact = [function] :stackSafe 1000 fact(10000) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 16d0264a0..7ad3b092c 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -21,11 +21,11 @@ f() + f() + f() //│ lambda$ = function lambda$(Handler$h$$instance, k) { //│ return runtime.safeCall(k()) //│ }; -//│ lambda = function lambda(Handler$h$$instance) { +//│ lambda = (undefined, function (Handler$h$$instance) { //│ return (k) => { //│ return lambda$(Handler$h$$instance, k) //│ } -//│ }; +//│ } ); //│ Handler$h$1 = class Handler$h$ extends Effect1 { //│ constructor() { //│ let tmp1; diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 3593b48ab..db64bd6ab 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -551,19 +551,19 @@ fun f(x, cond) = //│ lambda$1 = function lambda$(x1) { //│ return x1 //│ }; -//│ lambda = function lambda(x1) { +//│ lambda = (undefined, function (x1) { //│ return () => { //│ return lambda$1(x1) //│ } -//│ }; +//│ } ); //│ lambda$ = function lambda$(x1) { //│ return x1 //│ }; -//│ lambda1 = function lambda(x1) { +//│ lambda1 = (undefined, function (x1) { //│ return () => { //│ return lambda$(x1) //│ } -//│ }; +//│ } ); //│ f19 = function f(x1, cond) { //│ x1 = 1; //│ x1 = 2; diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 25d9ee0b2..7619e8df7 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -97,11 +97,11 @@ hi(0) //│ runtime.stackOffset = runtime.stackDepth; //│ return resume() //│ }; -//│ lambda = function lambda(StackDelay$$instance) { +//│ lambda = (undefined, function (StackDelay$$instance) { //│ return (resume) => { //│ return lambda$(StackDelay$$instance, resume) //│ } -//│ }; +//│ } ); //│ StackDelay$1 = class StackDelay$ extends runtime.StackDelay { //│ constructor() { //│ let tmp; @@ -286,11 +286,11 @@ sum(10000) //│ runtime.stackOffset = runtime.stackDepth; //│ return resume() //│ }; -//│ lambda1 = function lambda(StackDelay$$instance) { +//│ lambda1 = (undefined, function (StackDelay$$instance) { //│ return (resume) => { //│ return lambda$1(StackDelay$$instance, resume) //│ } -//│ }; +//│ } ); //│ StackDelay$3 = class StackDelay$2 extends runtime.StackDelay { //│ constructor() { //│ let tmp; diff --git a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls index 15e9e9b3b..302656e35 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cryptarithm2.mls @@ -36,7 +36,7 @@ fun guard(b) = if b then StateT(s => [Unit, s] :: Nil) else StateT(s => Nil) fun put(s) = StateT(x => [Unit, s] :: Nil) let get = StateT(s => [s, s] :: Nil) -//│ get = StateT([function lambda]) +//│ get = StateT([function]) class Digits(i: List[Int], c: List[[Char, Int]]) diff --git a/hkmc2/shared/src/test/mlscript/nofib/cse.mls b/hkmc2/shared/src/test/mlscript/nofib/cse.mls index 4011292cc..cfe6f1803 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/cse.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/cse.mls @@ -53,7 +53,7 @@ fun update(f) = s => [f(s), s] fun set_(s_) = s => [s_, s] let incr = update(x => x + 1) -//│ incr = [function lambda] +//│ incr = [function] class Node[T](a: T, b: List[Node[T]]) diff --git a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls index e8d6125ba..442068269 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/eliza.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/eliza.mls @@ -246,7 +246,7 @@ let initial = Nil then Nil [k, rs] :: t then [words(k), cycle(rs)] :: lscomp(t) [lscomp(respMsgs), cycle(repeatMsgs)] -//│ initial = [[[[["C","A","N"],["Y","O","U"]], Lazy([function lambda])],[[["C","A","N"],["I"]], Lazy([function lambda])],[[["Y","O","U"],["A","R","E"]], Lazy([function lambda])],[[["Y","O","U","'","R","E"]], Lazy([function lambda])],[[["I"],["D","O","N","'","T"]], Lazy([function lambda])],[[["I"],["F","E","E","L"]], Lazy([function lambda])],[[["W","H","Y"],["D","O","N","'","T"],["Y","O","U"]], Lazy([function lambda])],[[["W","H","Y"],["C","A","N","'","T"],["I"]], Lazy([function lambda])],[[["A","R","E"],["Y","O","U"]], Lazy([function lambda])],[[["I"],["C","A","N","'","T"]], Lazy([function lambda])],[[["I"],["A","M"]], Lazy([function lambda])],[[["I","'","M"]], Lazy([function lambda])],[[["Y","O","U"]], Lazy([function lambda])],[[["Y","E","S"]], Lazy([function lambda])],[[["N","O"]], Lazy([function lambda])],[[["C","O","M","P","U","T","E","R"]], Lazy([function lambda])],[[["C","O","M","P","U","T","E","R","S"]], Lazy([function lambda])],[[["I"],["W","A","N","T"]], Lazy([function lambda])],[[["W","H","A","T"]], Lazy([function lambda])],[[["H","O","W"]], Lazy([function lambda])],[[["W","H","O"]], Lazy([function lambda])],[[["W","H","E","R","E"]], Lazy([function lambda])],[[["W","H","E","N"]], Lazy([function lambda])],[[["N","A","M","E"]], Lazy([function lambda])],[[["W","H","Y"]], Lazy([function lambda])],[[["C","A","U","S","E"]], Lazy([function lambda])],[[["B","E","C","A","U","S","E"]], Lazy([function lambda])],[[["D","R","E","A","M"]], Lazy([function lambda])],[[["S","O","R","R","Y"]], Lazy([function lambda])],[[["H","I"]], Lazy([function lambda])],[[["D","R","E","A","M","S"]], Lazy([function lambda])],[[["M","A","Y","B","E"]], Lazy([function lambda])],[[["H","E","L","L","O"]], Lazy([function lambda])],[[["A","L","W","A","Y","S"]], Lazy([function lambda])],[[["Y","O","U","R"]], Lazy([function lambda])],[[["A","L","I","K","E"]], Lazy([function lambda])],[[["T","H","I","N","K"]], Lazy([function lambda])],[[["F","R","I","E","N","D","S"]], Lazy([function lambda])],[[["F","R","I","E","N","D"]], Lazy([function lambda])],[[], Lazy([function lambda])]], Lazy([function lambda])] +//│ initial = [[[[["C","A","N"],["Y","O","U"]], Lazy([function])],[[["C","A","N"],["I"]], Lazy([function])],[[["Y","O","U"],["A","R","E"]], Lazy([function])],[[["Y","O","U","'","R","E"]], Lazy([function])],[[["I"],["D","O","N","'","T"]], Lazy([function])],[[["I"],["F","E","E","L"]], Lazy([function])],[[["W","H","Y"],["D","O","N","'","T"],["Y","O","U"]], Lazy([function])],[[["W","H","Y"],["C","A","N","'","T"],["I"]], Lazy([function])],[[["A","R","E"],["Y","O","U"]], Lazy([function])],[[["I"],["C","A","N","'","T"]], Lazy([function])],[[["I"],["A","M"]], Lazy([function])],[[["I","'","M"]], Lazy([function])],[[["Y","O","U"]], Lazy([function])],[[["Y","E","S"]], Lazy([function])],[[["N","O"]], Lazy([function])],[[["C","O","M","P","U","T","E","R"]], Lazy([function])],[[["C","O","M","P","U","T","E","R","S"]], Lazy([function])],[[["I"],["W","A","N","T"]], Lazy([function])],[[["W","H","A","T"]], Lazy([function])],[[["H","O","W"]], Lazy([function])],[[["W","H","O"]], Lazy([function])],[[["W","H","E","R","E"]], Lazy([function])],[[["W","H","E","N"]], Lazy([function])],[[["N","A","M","E"]], Lazy([function])],[[["W","H","Y"]], Lazy([function])],[[["C","A","U","S","E"]], Lazy([function])],[[["B","E","C","A","U","S","E"]], Lazy([function])],[[["D","R","E","A","M"]], Lazy([function])],[[["S","O","R","R","Y"]], Lazy([function])],[[["H","I"]], Lazy([function])],[[["D","R","E","A","M","S"]], Lazy([function])],[[["M","A","Y","B","E"]], Lazy([function])],[[["H","E","L","L","O"]], Lazy([function])],[[["A","L","W","A","Y","S"]], Lazy([function])],[[["Y","O","U","R"]], Lazy([function])],[[["A","L","I","K","E"]], Lazy([function])],[[["T","H","I","N","K"]], Lazy([function])],[[["F","R","I","E","N","D","S"]], Lazy([function])],[[["F","R","I","E","N","D"]], Lazy([function])],[[], Lazy([function])]], Lazy([function])] fun prefix(xxs, yys) = if xxs is Nil then true diff --git a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls index 81e278242..0212dc307 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/lambda.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/lambda.mls @@ -17,7 +17,7 @@ fun myBind(m, f) = MyState(s => if myRunState(m, s) is [s_, a] then myRunState(f fun myReturn(a) = MyState(s => [s, a]) let myGet = MyState(s => [s, s]) -//│ myGet = MyState([function lambda]) +//│ myGet = MyState([function]) fun myEvalState(m, s) = if myRunState(m, s) is [s_, a] then a @@ -61,7 +61,7 @@ fun eqTerm(a, b) = if a is fun myMaybe(d, f, x) = if x is Some(x) then f(x) let incr = myReturn(Unit) -//│ incr = MyState([function lambda]) +//│ incr = MyState([function]) fun lookupVar(v) = fun lookup2(env) = myMaybe(dummy => throw Error("undefined"), x => x, lookup(v, env)) diff --git a/hkmc2/shared/src/test/mlscript/nofib/life.mls b/hkmc2/shared/src/test/mlscript/nofib/life.mls index e93b384e8..ebfc1ff64 100644 --- a/hkmc2/shared/src/test/mlscript/nofib/life.mls +++ b/hkmc2/shared/src/test/mlscript/nofib/life.mls @@ -51,7 +51,7 @@ let start = (lazy of () => LzNil) :: (lazy of () => LzNil) :: lzfy(0 :: 0 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: 1 :: 1 :: 1 :: 1 :: 1 :: 0 :: Nil) :: Nil -//│ start = [Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda]),Lazy([function lambda])] +//│ start = [Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function]),Lazy([function])] fun elt(a_b_c, d_e_f, g_h_i) = if a_b_c is [a, b, c] and d_e_f is [d, e, f] and g_h_i is [g, h, i] then let tot = a + b + c + d + f + g + h + i diff --git a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls index 3db4c0efd..71c2050f8 100644 --- a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls +++ b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls @@ -36,17 +36,17 @@ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.30: 1 //│ ╙── ^ -//│ = [function lambda] +//│ = [function] :sjs + //│ JS (unsanitized): -//│ let lambda1; lambda1 = function lambda(arg1, arg2) { return arg1 + arg2 }; lambda1 -//│ = [function lambda] +//│ let lambda1; lambda1 = (undefined, function (arg1, arg2) { return arg1 + arg2 } ); lambda1 +//│ = [function] * -//│ = [function lambda] +//│ = [function] :w :pt @@ -58,7 +58,7 @@ //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.53: 1 //│ ╙── ^ -//│ = [function lambda] +//│ = [function] fun (??) foo(x, y) = x + y diff --git a/hkmc2/shared/src/test/mlscript/std/Rendering.mls b/hkmc2/shared/src/test/mlscript/std/Rendering.mls index 7f5182875..fbdae341d 100644 --- a/hkmc2/shared/src/test/mlscript/std/Rendering.mls +++ b/hkmc2/shared/src/test/mlscript/std/Rendering.mls @@ -44,7 +44,7 @@ render(Object.create(null)) //│ = "[object]" render(x => x) -//│ = "[function lambda]" +//│ = "[function]" class Foo diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls index 9dfbe4bc8..27beff04a 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls @@ -88,8 +88,8 @@ fun //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.82: @inline let //│ ╙── ^^^^^^ -//│ abs = [function lambda] -//│ clamp = [function lambda] +//│ abs = [function] +//│ clamp = [function] :w // Only the first variable is annotated with @inline. diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls index efb9a8481..53a5e778a 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -10,7 +10,7 @@ class B x => if x is Pair(A, B) then 1 //│ JS (unsanitized): //│ let lambda; -//│ lambda = function lambda(x) { +//│ lambda = (undefined, function (x) { //│ let param0, param1; //│ if (x instanceof Pair1.class) { //│ param0 = x.a; @@ -27,9 +27,9 @@ x => if x is Pair(A, B) then 1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ }; +//│ } ); //│ lambda -//│ = [function lambda] +//│ = [function] // :e // FIXME: should be an exhaustiveness error From bcfb48cbb4b7d3c72243ddbc046cb0c4a42963f9 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 19:23:26 +0800 Subject: [PATCH 121/127] cosmetic changes, update checkArgs --- .../main/scala/hkmc2/codegen/js/JSBuilder.scala | 10 ++++++---- .../src/main/scala/hkmc2/semantics/Symbol.scala | 2 +- .../mlscript/basics/BadMemberProjections.mls | 8 ++++---- .../test/mlscript/basics/MemberProjections.mls | 6 +++--- .../src/test/mlscript/basics/MiscArrayTests.mls | 2 +- .../shared/src/test/mlscript/bbml/bbCodeGen.mls | 8 ++++---- .../shared/src/test/mlscript/bbml/bbGetters.mls | 2 +- .../shared/src/test/mlscript/codegen/BadNew.mls | 2 +- .../src/test/mlscript/codegen/BuiltinOps.mls | 6 +++--- .../src/test/mlscript/codegen/CaseShorthand.mls | 16 +++++----------- .../src/test/mlscript/codegen/ClassMatching.mls | 2 +- .../src/test/mlscript/codegen/DelayedLetInit.mls | 2 +- .../shared/src/test/mlscript/codegen/Getters.mls | 2 +- .../shared/src/test/mlscript/codegen/Hygiene.mls | 2 +- .../src/test/mlscript/codegen/IfThenElse.mls | 6 +++--- .../src/test/mlscript/codegen/InlineLambdas.mls | 12 ++++++------ .../shared/src/test/mlscript/codegen/Lambdas.mls | 4 ++-- .../src/test/mlscript/codegen/OptMatch.mls | 2 +- .../src/test/mlscript/codegen/PartialApps.mls | 6 +++--- .../src/test/mlscript/codegen/SanityChecks.mls | 8 ++++---- hkmc2/shared/src/test/mlscript/codegen/SetIn.mls | 4 ++-- hkmc2/shared/src/test/mlscript/codegen/While.mls | 4 ++-- hkmc2/shared/src/test/mlscript/interop/Array.mls | 2 +- .../src/test/mlscript/lifter/ClassInFun.mls | 2 +- .../shared/src/test/mlscript/lifter/FunInFun.mls | 4 ++-- .../src/test/mlscript/lifter/StackSafetyLift.mls | 4 ++-- .../src/test/mlscript/parser/PrefixOps.mls | 2 +- .../ucs/normalization/SimplePairMatches.mls | 2 +- 28 files changed, 64 insertions(+), 68 deletions(-) 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 2db1880c8..ddbd7002c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -181,10 +181,12 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: case FunDefn(own, sym, ps :: pss, bod) => val result = pss.foldRight(bod): case (ps, block) => - Return(Lam(ps, block), false) - val (params, bodyDoc) = setupFunction(some(sym.nme), ps, result) - if sym.isTmp then - doc"${getVar(sym)} = (undefined, function ($params) ${ braced(bodyDoc) } );" + Return(Lam(ps, block), false) + val name = if sym.nameIsTemp then none else some(sym.nme) + val (params, bodyDoc) = setupFunction(name, ps, result) + if sym.nameIsTemp then + // in JS, let name = (0, function (args) => {} ) prevents function's name from being bound to `name` + doc"${getVar(sym)} = (undefined, function ($params) ${ braced(bodyDoc) });" else doc"${getVar(sym)} = function ${sym.nme}($params) ${ braced(bodyDoc) };" case ClsLikeDefn(ownr, isym, sym, kind, paramsOpt, auxParams, par, mtds, privFlds, _pubFlds, preCtor, ctor) => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala index d34f01863..f45d5bb3d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala @@ -131,7 +131,7 @@ class BuiltinSymbol /** This is the outside-facing symbol associated to a possibly-overloaded * definition living in a block – e.g., a module or class. */ -class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree], val isTmp: Bool = false)(using State) +class BlockMemberSymbol(val nme: Str, val trees: Ls[Tree], val nameIsTemp: Bool = false)(using State) extends MemberSymbol[Definition]: def toLoc: Option[Loc] = Loc(trees) diff --git a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls index 2607dc965..1415b5561 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadMemberProjections.mls @@ -13,7 +13,7 @@ //│ let lambda; //│ lambda = (undefined, function (self, ...args) { //│ return runtime.safeCall(self.x(...args)) -//│ } ); +//│ }); //│ lambda //│ = [function] @@ -28,14 +28,14 @@ //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS: //│ lambda1 = (undefined, function (...args1) { -//│ globalThis.Predef.checkArgs("lambda", 1, false, args1.length); +//│ globalThis.Predef.checkArgs("", 1, false, args1.length); //│ let self = args1[0]; //│ let args = globalThis.Predef.tupleSlice(args1, 1, 0); //│ return runtime.safeCall(self.x(...args)) -//│ } ); +//│ }); //│ block$res2 = runtime.checkCall(lambda1()); //│ undefined -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 +//│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 fun (::) f(a, b) = [a, b] diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index ae97dbff2..ccd065c45 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -49,7 +49,7 @@ let m = M.Foo::m //│ let m, lambda5; //│ lambda5 = (undefined, function (self, ...args) { //│ return self.m(...args) -//│ } ); +//│ }); //│ m = lambda5; //│ m = [function] @@ -95,7 +95,7 @@ foo.M.Foo::m() //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected at least 1 argument but got 0 +//│ ═══[RUNTIME ERROR] Error: Function expected at least 1 argument but got 0 :e :re @@ -135,7 +135,7 @@ Foo::n(foo, 2) //│ let lambda10; //│ lambda10 = (undefined, function (self, ...args) { //│ return runtime.safeCall(self.n(...args)) -//│ } ); +//│ }); //│ lambda10(foo, 2) //│ = 125 diff --git a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls index 07281e3ce..38085356a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MiscArrayTests.mls @@ -21,7 +21,7 @@ xs :re xs.map(x => x * 2) -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 1 argument but got 3 +//│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 xs.map((x, i, a) => x * 2) //│ = [4, 2, 0] diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index c5ee35a40..dc84d238f 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -53,7 +53,7 @@ false :sjs (x => x): [T] -> T -> T //│ JS (unsanitized): -//│ let lambda; lambda = (undefined, function (x1) { return x1 } ); lambda +//│ let lambda; lambda = (undefined, function (x1) { return x1 }); lambda //│ = [function] //│ Type: ['T] -> ('T) ->{⊥} 'T @@ -162,7 +162,7 @@ fun pow(x) = case //│ tmp4 = runtime.safeCall(tmp2(tmp3)); //│ return x1 * tmp4 //│ } -//│ } ); +//│ }); //│ return lambda1 //│ }; //│ Type: ⊤ @@ -184,7 +184,7 @@ fun not = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ return lambda1 //│ }; //│ Type: ⊤ @@ -218,7 +218,7 @@ fun fact = case //│ tmp5 = tmp3(tmp4); //│ return n * tmp5 //│ } -//│ } ); +//│ }); //│ return lambda1 //│ }; //│ Type: ⊤ diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls index cdfdb63e3..81e3a81e0 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls @@ -93,7 +93,7 @@ fun test2() = //│ tmp3 = n - 1; //│ tmp4 = tmp2(tmp3); //│ return tmp4 + 1 -//│ } ); +//│ }); //│ return lambda //│ }; //│ tmp1 = funny(); diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls index 03d36f92c..42d5257f5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadNew.mls @@ -14,7 +14,7 @@ new 2 :sjs new 2 + 2 //│ JS (unsanitized): -//│ let lambda; lambda = (undefined, function (arg1, arg2) { return arg1 + arg2 } ); new lambda(2, 2) +//│ let lambda; lambda = (undefined, function (arg1, arg2) { return arg1 + arg2 }); new lambda(2, 2) //│ = [object Object] :re diff --git a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls index 6d2af5a80..a233e9eef 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BuiltinOps.mls @@ -77,10 +77,10 @@ id(+)(1) //│ let tmp1, lambda4; //│ lambda4 = (undefined, function (arg1, arg2) { //│ return arg1 + arg2 -//│ } ); +//│ }); //│ tmp1 = Predef.id(lambda4); //│ runtime.safeCall(tmp1(1)) -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 +//│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 fun (+) lol(a, b) = [a, b] @@ -98,7 +98,7 @@ id(~)(2) //│ let tmp2, lambda5; //│ lambda5 = (undefined, function (arg) { //│ return ~ arg -//│ } ); +//│ }); //│ tmp2 = Predef.id(lambda5); //│ runtime.safeCall(tmp2(2)) //│ = -3 diff --git a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls index cdc4c9b68..f71cc743a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CaseShorthand.mls @@ -7,13 +7,7 @@ case x then x :sjs case { x then x } //│ JS (unsanitized): -//│ let lambda1; -//│ lambda1 = (undefined, function (caseScrut) { -//│ let x; -//│ x = caseScrut; -//│ return x -//│ } ); -//│ lambda1 +//│ let lambda1; lambda1 = (undefined, function (caseScrut) { let x; x = caseScrut; return x }); lambda1 //│ = [function] :sjs @@ -27,7 +21,7 @@ x => if x is //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ lambda2 //│ = [function] @@ -41,7 +35,7 @@ case 0 then true //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ lambda3 //│ = [function] @@ -76,7 +70,7 @@ case //│ } else { //│ return false //│ } -//│ } ); +//│ }); //│ lambda10 //│ = [function] @@ -97,7 +91,7 @@ val isDefined = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ tmp5 = lambda11; //│ isDefined = tmp5; //│ isDefined = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index 409e22227..10668a8d3 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -67,7 +67,7 @@ x => if x is Some(x) then x //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ lambda //│ = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index 8a876b707..b1933feaf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -55,7 +55,7 @@ f let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f1, lambda; lambda = (undefined, function (x1) { return x1 + 1 } ); f1 = lambda; +//│ let f1, lambda; lambda = (undefined, function (x1) { return x1 + 1 }); f1 = lambda; //│ f = [function] f(1) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index d01b5f6d7..f2b515890 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -170,7 +170,7 @@ fun baz() = //│ tmp3 = tmp1 + tmp2; //│ tmp4 = z(); //│ return tmp3 + tmp4 -//│ } ); +//│ }); //│ return lambda //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index fce701ac8..74ebed73e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -58,7 +58,7 @@ f() //│ x = 1; //│ lambda = (undefined, function () { //│ return x -//│ } ); +//│ }); //│ f = lambda; //│ x1 = 2; //│ runtime.safeCall(f()) diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index 779694f3b..b8c74db6b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -16,7 +16,7 @@ let f = x => if x then print("ok") else print("ko") //│ } else { //│ return Predef.print("ko") //│ } -//│ } ); +//│ }); //│ f = lambda; //│ f = [function] @@ -40,7 +40,7 @@ let f = x => print((if x then "ok" else "ko") + "!") //│ } //│ tmp2 = tmp1 + "!"; //│ return Predef.print(tmp2) -//│ } ); +//│ }); //│ tmp = lambda1; //│ f1 = tmp; //│ f = [function] @@ -58,7 +58,7 @@ let f = x => print((if x and x then "ok" else "ko") + "!") //│ } //│ tmp3 = tmp2 + "!"; //│ return Predef.print(tmp3) -//│ } ); +//│ }); //│ tmp1 = lambda2; //│ f2 = tmp1; //│ f = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls index 3e4308ff9..3c5732e9c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/InlineLambdas.mls @@ -11,7 +11,7 @@ //│ tmp2 = tmp1 + 1; //│ tmp3 = tmp2 + 1; //│ return tmp3 + 1 -//│ } ); +//│ }); //│ lambda(1) //│ = 6 @@ -27,7 +27,7 @@ //│ tmp4 = tmp3 + 1; //│ tmp5 = tmp4 + 1; //│ return tmp5 + 1 -//│ } ); +//│ }); //│ tmp = lambda1; //│ tmp(1) //│ = 7 @@ -35,13 +35,13 @@ :sjs (x => x) + 1 //│ JS (unsanitized): -//│ let lambda2; lambda2 = (undefined, function (x) { return x } ); lambda2 + 1 -//│ = "function (...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }1" +//│ let lambda2; lambda2 = (undefined, function (x) { return x }); lambda2 + 1 +//│ = "function (...args) { globalThis.Predef.checkArgs(\"\", 1, true, args.length); let x = args[0]; return x }1" :sjs 1 + (x => x) //│ JS (unsanitized): -//│ let lambda3; lambda3 = (undefined, function (x) { return x } ); 1 + lambda3 -//│ = "1function (...args) { globalThis.Predef.checkArgs(\"lambda\", 1, true, args.length); let x = args[0]; return x }" +//│ let lambda3; lambda3 = (undefined, function (x) { return x }); 1 + lambda3 +//│ = "1function (...args) { globalThis.Predef.checkArgs(\"\", 1, true, args.length); let x = args[0]; return x }" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls index 76b38d69c..d05c9f78c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Lambdas.mls @@ -8,14 +8,14 @@ x => let y = x y //│ JS (unsanitized): -//│ let lambda; lambda = (undefined, function (x) { let y; y = x; return y } ); lambda +//│ let lambda; lambda = (undefined, function (x) { let y; y = x; return y }); lambda //│ = [function] :todo (acc, _) => acc //│ JS (unsanitized): -//│ let lambda1; lambda1 = (undefined, function (acc, _) { return acc } ); lambda1 +//│ let lambda1; lambda1 = (undefined, function (acc, _) { return acc }); lambda1 //│ = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls index 3ac603997..5e49f03ea 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/OptMatch.mls @@ -44,7 +44,7 @@ val isDefined = case //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ tmp1 = lambda; //│ isDefined1 = tmp1; //│ isDefined = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 3e9094094..9bf9f9347 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -12,7 +12,7 @@ f(2) :sjs let f = foo(1, _, _) //│ JS (unsanitized): -//│ let f1, lambda1; lambda1 = (undefined, function (_, _1) { return foo(1, _, _1) } ); f1 = lambda1; +//│ let f1, lambda1; lambda1 = (undefined, function (_, _1) { return foo(1, _, _1) }); f1 = lambda1; //│ f = [function] f(2, 3) @@ -151,7 +151,7 @@ _ - 2 <| 1 :sjs 1 . _ - 2 //│ JS (unsanitized): -//│ let lambda30; lambda30 = (undefined, function (_) { return Predef.passTo(1, _) } ); lambda30 - 2 +//│ let lambda30; lambda30 = (undefined, function (_) { return Predef.passTo(1, _) }); lambda30 - 2 //│ = NaN :sjs @@ -160,7 +160,7 @@ _ - 2 <| 1 //│ let tmp7, lambda31; //│ lambda31 = (undefined, function (_) { //│ return _ - 2 -//│ } ); +//│ }); //│ tmp7 = Predef.passTo(1, lambda31); //│ runtime.safeCall(tmp7()) //│ = -1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index 2348144e8..a54499ad4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -64,7 +64,7 @@ let f = (x, y) => x + y in f(2) //│ JS: //│ lambda = (undefined, function (x, y) { //│ return x + y -//│ } ); +//│ }); //│ f3 = lambda; //│ block$res5 = runtime.safeCall(f3(2)); //│ undefined @@ -76,15 +76,15 @@ let f = (x, y) => x + y f(2) //│ JS: //│ lambda1 = (undefined, function (...args) { -//│ globalThis.Predef.checkArgs("lambda", 2, true, args.length); +//│ globalThis.Predef.checkArgs("", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y -//│ } ); +//│ }); //│ f4 = lambda1; //│ block$res6 = runtime.safeCall(f4(2)); //│ undefined -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 2 arguments but got 1 +//│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 //│ f = [function] diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 206dae241..cd21b092e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -82,7 +82,7 @@ example() //│ x2 = 0; //│ lambda = (undefined, function () { //│ return x2 -//│ } ); +//│ }); //│ get_x = lambda; //│ old1 = x2; //│ try { @@ -123,7 +123,7 @@ example() //│ x2 = 0; //│ lambda = (undefined, function () { //│ return x2 -//│ } ); +//│ }); //│ get_x = lambda; //│ old1 = x2; //│ try { diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index c842e3d01..cfbb8f980 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -18,7 +18,7 @@ //│ break; //│ } //│ return tmp -//│ } ); +//│ }); //│ lambda //│ = [function] @@ -122,7 +122,7 @@ while //│ break; //│ } //│ return tmp20 -//│ } ); +//│ }); //│ lambda2 //│ = [function] diff --git a/hkmc2/shared/src/test/mlscript/interop/Array.mls b/hkmc2/shared/src/test/mlscript/interop/Array.mls index ec81be309..e322dac46 100644 --- a/hkmc2/shared/src/test/mlscript/interop/Array.mls +++ b/hkmc2/shared/src/test/mlscript/interop/Array.mls @@ -8,7 +8,7 @@ let arr = [true, false] // * Array methods annoyingly supply extra arguments, such as the index and the array itself :re arr.map(_ === false) -//│ ═══[RUNTIME ERROR] Error: Function 'lambda' expected 1 argument but got 3 +//│ ═══[RUNTIME ERROR] Error: Function expected 1 argument but got 3 :todo arr.map((e, ...) => e === false) diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 7ad3b092c..f9723e7dc 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -25,7 +25,7 @@ f() + f() + f() //│ return (k) => { //│ return lambda$(Handler$h$$instance, k) //│ } -//│ } ); +//│ }); //│ Handler$h$1 = class Handler$h$ extends Effect1 { //│ constructor() { //│ let tmp1; diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index db64bd6ab..495f172ea 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -555,7 +555,7 @@ fun f(x, cond) = //│ return () => { //│ return lambda$1(x1) //│ } -//│ } ); +//│ }); //│ lambda$ = function lambda$(x1) { //│ return x1 //│ }; @@ -563,7 +563,7 @@ fun f(x, cond) = //│ return () => { //│ return lambda$(x1) //│ } -//│ } ); +//│ }); //│ f19 = function f(x1, cond) { //│ x1 = 1; //│ x1 = 2; diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 7619e8df7..01113e558 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -101,7 +101,7 @@ hi(0) //│ return (resume) => { //│ return lambda$(StackDelay$$instance, resume) //│ } -//│ } ); +//│ }); //│ StackDelay$1 = class StackDelay$ extends runtime.StackDelay { //│ constructor() { //│ let tmp; @@ -290,7 +290,7 @@ sum(10000) //│ return (resume) => { //│ return lambda$1(StackDelay$$instance, resume) //│ } -//│ } ); +//│ }); //│ StackDelay$3 = class StackDelay$2 extends runtime.StackDelay { //│ constructor() { //│ let tmp; diff --git a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls index 71c2050f8..1c7cc4bbb 100644 --- a/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls +++ b/hkmc2/shared/src/test/mlscript/parser/PrefixOps.mls @@ -42,7 +42,7 @@ :sjs + //│ JS (unsanitized): -//│ let lambda1; lambda1 = (undefined, function (arg1, arg2) { return arg1 + arg2 } ); lambda1 +//│ let lambda1; lambda1 = (undefined, function (arg1, arg2) { return arg1 + arg2 }); lambda1 //│ = [function] * diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls index 53a5e778a..0eef5c4d0 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/SimplePairMatches.mls @@ -27,7 +27,7 @@ x => if x is Pair(A, B) then 1 //│ } else { //│ throw new globalThis.Error("match error"); //│ } -//│ } ); +//│ }); //│ lambda //│ = [function] From 7edb5ba26d807b0d4fa27b247f19cb109a18cef1 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 19:44:50 +0800 Subject: [PATCH 122/127] preserve original lambda semantics --- .../scala/hkmc2/codegen/LambdaRewriter.scala | 32 ++++++++++------- .../mlscript/basics/MemberProjections.mls | 36 +++++++++---------- .../src/test/mlscript/basics/OpBlocks.mls | 2 +- .../test/mlscript/codegen/DelayedLetInit.mls | 10 +++--- .../src/test/mlscript/codegen/Hygiene.mls | 17 +++------ .../src/test/mlscript/codegen/IfThenElse.mls | 26 +++++++------- .../src/test/mlscript/codegen/ImportedOps.mls | 2 +- .../src/test/mlscript/codegen/PartialApps.mls | 24 ++++++------- .../src/test/mlscript/codegen/ReboundLet.mls | 2 +- .../test/mlscript/codegen/SanityChecks.mls | 21 +++++------ .../src/test/mlscript/codegen/SetIn.mls | 16 ++++----- .../src/test/mlscript/codegen/Spreads.mls | 2 +- .../mlscript/handlers/ReturnInHandler.mls | 2 +- .../syntax/annotations/Declarations.mls | 4 +-- 14 files changed, 94 insertions(+), 102 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala index 86f4ad042..1351c5b22 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/LambdaRewriter.scala @@ -9,24 +9,32 @@ import semantics.Elaborator.State object LambdaRewriter: def desugar(b: Block)(using State) = - def rewriteOneBlk(b: Block) = - var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil - val lambdaRewriter = new BlockDataTransformer(SymbolSubst()): - override def applyValue(v: Value): Value = v match - case lam: Value.Lam => - val sym = BlockMemberSymbol("lambda", Nil, true) - lambdasList ::= (sym -> super.applyLam(lam)) - Value.Ref(sym) - case _ => super.applyValue(v) - val blk = lambdaRewriter.applyBlock(b) - (blk, lambdasList) + def rewriteOneBlk(b: Block) = b match + case Assign(lhs, Value.Lam(params, body), rest) if !lhs.isInstanceOf[TempSymbol] => + val newSym = BlockMemberSymbol(lhs.nme, Nil, false) + val blk = blockBuilder + .define(FunDefn(N, newSym, params :: Nil, body)) + .assign(lhs, newSym.asPath) + .rest(rest) + (blk, Nil) + case _ => + var lambdasList: List[(BlockMemberSymbol, Value.Lam)] = Nil + val lambdaRewriter = new BlockDataTransformer(SymbolSubst()): + override def applyValue(v: Value): Value = v match + case lam: Value.Lam => + val sym = BlockMemberSymbol("lambda", Nil, true) + lambdasList ::= (sym -> super.applyLam(lam)) + Value.Ref(sym) + case _ => super.applyValue(v) + val blk = lambdaRewriter.applyBlock(b) + (blk, lambdasList) val transformer = new BlockTransformer(SymbolSubst()): override def applyBlock(b: Block): Block = val (newBlk, lambdasList) = rewriteOneBlk(b) val lambdaDefns = lambdasList.map: case (sym, Value.Lam(params, body)) => - FunDefn(None, sym, params :: Nil, body) + FunDefn(N, sym, params :: Nil, body) val ret = lambdaDefns.foldLeft(newBlk): case (acc, defn) => Define(defn, acc) super.applyBlock(ret) diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index ccd065c45..a9a9780b8 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -46,12 +46,8 @@ M.Foo:: n(foo, 2) :sjs let m = M.Foo::m //│ JS (unsanitized): -//│ let m, lambda5; -//│ lambda5 = (undefined, function (self, ...args) { -//│ return self.m(...args) -//│ }); -//│ m = lambda5; -//│ m = [function] +//│ let m, m1; m1 = function m(self, ...args) { return self.m(...args) }; m = m1; +//│ m = [function m] m(foo) //│ = 124 @@ -80,10 +76,10 @@ do :e foo.Foo#m() //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.81: foo.Foo#m() +//│ ║ l.77: foo.Foo#m() //│ ╙── ^^^^ //│ ╔══[ERROR] Identifier `Foo` does not name a known class symbol. -//│ ║ l.81: foo.Foo#m() +//│ ║ l.77: foo.Foo#m() //│ ╙── ^^^^ //│ = 124 @@ -91,7 +87,7 @@ foo.Foo#m() :re foo.M.Foo::m() //│ ╔══[ERROR] Selection is not a known class. -//│ ║ l.92: foo.M.Foo::m() +//│ ║ l.88: foo.M.Foo::m() //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -101,20 +97,20 @@ foo.M.Foo::m() :re foo.M.Foo#m() //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.102: foo.M.Foo#m() -//│ ╙── ^^^^ +//│ ║ l.98: foo.M.Foo#m() +//│ ╙── ^^^^ //│ ╔══[ERROR] Identifier `Foo` does not name a known class symbol. -//│ ║ l.102: foo.M.Foo#m() -//│ ╙── ^^^^ +//│ ║ l.98: foo.M.Foo#m() +//│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'M' yielded 'undefined' :e Foo::m //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.112: Foo::m +//│ ║ l.108: Foo::m //│ ╙── ^^^ //│ ╔══[ERROR] Identifier is not a known class. -//│ ║ l.112: Foo::m +//│ ║ l.108: Foo::m //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. @@ -124,19 +120,19 @@ Foo::m :sjs Foo::n(foo, 2) //│ ╔══[ERROR] Name not found: Foo -//│ ║ l.125: Foo::n(foo, 2) +//│ ║ l.121: Foo::n(foo, 2) //│ ╙── ^^^ //│ ╔══[ERROR] Identifier is not a known class. -//│ ║ l.125: Foo::n(foo, 2) +//│ ║ l.121: Foo::n(foo, 2) //│ ║ ^^^ //│ ╟── Note: any expression of the form `‹expression›::‹identifier›` is a member projection; //│ ╙── add a space before ‹identifier› to make it an operator application. //│ JS (unsanitized): -//│ let lambda10; -//│ lambda10 = (undefined, function (self, ...args) { +//│ let lambda9; +//│ lambda9 = (undefined, function (self, ...args) { //│ return runtime.safeCall(self.n(...args)) //│ }); -//│ lambda10(foo, 2) +//│ lambda9(foo, 2) //│ = 125 diff --git a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls index 4f0c59472..2b6107405 100644 --- a/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls +++ b/hkmc2/shared/src/test/mlscript/basics/OpBlocks.mls @@ -31,7 +31,7 @@ let f = x => x * 2 -//│ f = [function] +//│ f = [function f] 2 + 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index b1933feaf..82531d939 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -55,8 +55,8 @@ f let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f1, lambda; lambda = (undefined, function (x1) { return x1 + 1 }); f1 = lambda; -//│ f = [function] +//│ let f1, f2; f2 = function f(x1) { return x1 + 1 }; f1 = f2; +//│ f = [function f] f(1) //│ JS (unsanitized): @@ -111,11 +111,11 @@ else fun f() = foo = 42 //│ JS (unsanitized): -//│ let f2; f2 = function f() { foo2 = 42; return runtime.Unit }; +//│ let f3; f3 = function f() { foo2 = 42; return runtime.Unit }; f() //│ JS (unsanitized): -//│ f2() +//│ f3() foo //│ JS (unsanitized): @@ -129,6 +129,6 @@ fun f() = foo = 0 //│ ║ l.127: fun f() = foo = 0 //│ ╙── ^ //│ JS (unsanitized): -//│ let f3; f3 = function f() { return foo2 }; +//│ let f4; f4 = function f() { return foo2 }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 74ebed73e..3f2b302a0 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -54,16 +54,9 @@ let f = () => x let x = 2 f() //│ JS (unsanitized): -//│ let x, f, x1, lambda; -//│ x = 1; -//│ lambda = (undefined, function () { -//│ return x -//│ }); -//│ f = lambda; -//│ x1 = 2; -//│ runtime.safeCall(f()) +//│ let x, f, x1, f1; x = 1; f1 = function f() { return x }; f = f1; x1 = 2; runtime.safeCall(f()) //│ = 1 -//│ f = [function] +//│ f = [function f] //│ x = 2 @@ -73,10 +66,10 @@ module Test with val x = 1 let x = 2 //│ ╔══[ERROR] Name 'x' is already used -//│ ║ l.74: let x = 2 +//│ ║ l.67: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.73: val x = 1 +//│ ║ l.66: val x = 1 //│ ╙── ^^^^^ //│ JS: //│ Test4 = class Test3 { @@ -204,7 +197,7 @@ Whoops.Whoops.g() :e Runtime //│ ╔══[ERROR] Name not found: Runtime -//│ ║ l.205: Runtime +//│ ║ l.198: Runtime //│ ╙── ^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index b8c74db6b..e862fa219 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -9,16 +9,16 @@ if true then 1 else 0 :sjs let f = x => if x then print("ok") else print("ko") //│ JS (unsanitized): -//│ let f, lambda; -//│ lambda = (undefined, function (x) { +//│ let f, f1; +//│ f1 = function f(x) { //│ if (x === true) { //│ return Predef.print("ok") //│ } else { //│ return Predef.print("ko") //│ } -//│ }); -//│ f = lambda; -//│ f = [function] +//│ }; +//│ f = f1; +//│ f = [function f] f(true) //│ > ok @@ -30,8 +30,8 @@ f(false) :sjs let f = x => print((if x then "ok" else "ko") + "!") //│ JS (unsanitized): -//│ let f1, tmp, lambda1; -//│ lambda1 = (undefined, function (x) { +//│ let f2, tmp, lambda; +//│ lambda = (undefined, function (x) { //│ let tmp1, tmp2; //│ if (x === true) { //│ tmp1 = "ok"; @@ -41,15 +41,15 @@ let f = x => print((if x then "ok" else "ko") + "!") //│ tmp2 = tmp1 + "!"; //│ return Predef.print(tmp2) //│ }); -//│ tmp = lambda1; -//│ f1 = tmp; +//│ tmp = lambda; +//│ f2 = tmp; //│ f = [function] :sjs let f = x => print((if x and x then "ok" else "ko") + "!") //│ JS (unsanitized): -//│ let f2, tmp1, lambda2; -//│ lambda2 = (undefined, function (x) { +//│ let f3, tmp1, lambda1; +//│ lambda1 = (undefined, function (x) { //│ let tmp2, tmp3; //│ if (x === true) { //│ tmp2 = "ok"; @@ -59,8 +59,8 @@ let f = x => print((if x and x then "ok" else "ko") + "!") //│ tmp3 = tmp2 + "!"; //│ return Predef.print(tmp3) //│ }); -//│ tmp1 = lambda2; -//│ f2 = tmp1; +//│ tmp1 = lambda1; +//│ f3 = tmp1; //│ f = [function] // --- TODO: What we want --- // this.f = (x) => { diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index a88049acc..5d625f575 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -24,7 +24,7 @@ let name = "_" //│ name = "_" let display(balance) = balance -//│ display = [function] +//│ display = [function display] "b" ~ display("-") //│ = "b-" diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index 9bf9f9347..baff82f9c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls @@ -4,7 +4,7 @@ fun foo(x, y, z) = [x, y, z] let f = foo(1, _, 3) -//│ f = [function] +//│ f = [function f] f(2) //│ = [1, 2, 3] @@ -12,14 +12,14 @@ f(2) :sjs let f = foo(1, _, _) //│ JS (unsanitized): -//│ let f1, lambda1; lambda1 = (undefined, function (_, _1) { return foo(1, _, _1) }); f1 = lambda1; -//│ f = [function] +//│ let f2, f3; f3 = function f(_, _1) { return foo(1, _, _1) }; f2 = f3; +//│ f = [function f] f(2, 3) //│ = [1, 2, 3] let g = f(_, 3) -//│ g = [function] +//│ g = [function g] g(2) //│ = [1, 2, 3] @@ -45,14 +45,14 @@ foo(..._) let h = _ - 2 -//│ h = [function] +//│ h = [function h] h(1) //│ = -1 let i = _(0, 1, _) -//│ i = [function] +//│ i = [function i] i((x, y, z) => x + y + z, 2) //│ = 3 @@ -97,12 +97,12 @@ let j = _.x(123) :todo let j = _.x(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function] +//│ j = [function j] :todo // really support this? let j = _.x.y(1, _) //│ ═══[ERROR] Illegal position for '_' placeholder. -//│ j = [function] +//│ j = [function j] class C(a, b, c) @@ -151,17 +151,17 @@ _ - 2 <| 1 :sjs 1 . _ - 2 //│ JS (unsanitized): -//│ let lambda30; lambda30 = (undefined, function (_) { return Predef.passTo(1, _) }); lambda30 - 2 +//│ let lambda23; lambda23 = (undefined, function (_) { return Predef.passTo(1, _) }); lambda23 - 2 //│ = NaN :sjs 1 . (_ - 2)() //│ JS (unsanitized): -//│ let tmp7, lambda31; -//│ lambda31 = (undefined, function (_) { +//│ let tmp7, lambda24; +//│ lambda24 = (undefined, function (_) { //│ return _ - 2 //│ }); -//│ tmp7 = Predef.passTo(1, lambda31); +//│ tmp7 = Predef.passTo(1, lambda24); //│ runtime.safeCall(tmp7()) //│ = -1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls index 03c498c27..a6ac31261 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ReboundLet.mls @@ -15,7 +15,7 @@ foo() let x = 1 let f = () => x -//│ f = [function] +//│ f = [function f] //│ x = 1 f() diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index a54499ad4..2b514aa2c 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -62,12 +62,7 @@ f(3)(4) :noSanityCheck let f = (x, y) => x + y in f(2) //│ JS: -//│ lambda = (undefined, function (x, y) { -//│ return x + y -//│ }); -//│ f3 = lambda; -//│ block$res5 = runtime.safeCall(f3(2)); -//│ undefined +//│ f4 = function f(x, y) { return x + y }; f3 = f4; block$res5 = runtime.safeCall(f3(2)); undefined //│ = NaN :ssjs @@ -75,17 +70,17 @@ let f = (x, y) => x + y in f(2) let f = (x, y) => x + y f(2) //│ JS: -//│ lambda1 = (undefined, function (...args) { -//│ globalThis.Predef.checkArgs("", 2, true, args.length); +//│ f6 = function f(...args) { +//│ globalThis.Predef.checkArgs("f", 2, true, args.length); //│ let x = args[0]; //│ let y = args[1]; //│ return x + y -//│ }); -//│ f4 = lambda1; -//│ block$res6 = runtime.safeCall(f4(2)); +//│ }; +//│ f5 = f6; +//│ block$res6 = runtime.safeCall(f5(2)); //│ undefined -//│ ═══[RUNTIME ERROR] Error: Function expected 2 arguments but got 1 -//│ f = [function] +//│ ═══[RUNTIME ERROR] Error: Function 'f' expected 2 arguments but got 1 +//│ f = [function f] :expect NaN diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index cd21b092e..b4853b7c4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -78,12 +78,12 @@ example() //│ JS (unsanitized): //│ let example2; //│ example2 = function example() { -//│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, lambda; +//│ let x2, get_x, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, get_x1; //│ x2 = 0; -//│ lambda = (undefined, function () { +//│ get_x1 = function get_x() { //│ return x2 -//│ }); -//│ get_x = lambda; +//│ }; +//│ get_x = get_x1; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; @@ -119,12 +119,12 @@ example() //│ JS (unsanitized): //│ let example3; //│ example3 = function example() { -//│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, lambda; +//│ let x2, get_x, y, old1, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, get_x1; //│ x2 = 0; -//│ lambda = (undefined, function () { +//│ get_x1 = function get_x() { //│ return x2 -//│ }); -//│ get_x = lambda; +//│ }; +//│ get_x = get_x1; //│ old1 = x2; //│ try { //│ tmp6 = x2 + 1; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index 70167d60a..63ac713cf 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -28,7 +28,7 @@ foo(...a) let f = (...xs) => xs -//│ f = [function] +//│ f = [function f] f(a) //│ = [[1, 2, 3]] diff --git a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls index fdde5dc1a..4c724b153 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/ReturnInHandler.mls @@ -35,7 +35,7 @@ let l = () => fun foo()(r) = r(()) return 3 4 -//│ l = [function] +//│ l = [function l] l() //│ = 3 diff --git a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls index 27beff04a..9443ae13a 100644 --- a/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls +++ b/hkmc2/shared/src/test/mlscript/syntax/annotations/Declarations.mls @@ -88,8 +88,8 @@ fun //│ ╔══[WARNING] This annotation has no effect. //│ ║ l.82: @inline let //│ ╙── ^^^^^^ -//│ abs = [function] -//│ clamp = [function] +//│ abs = [function abs] +//│ clamp = [function clamp] :w // Only the first variable is annotated with @inline. From ff30d68133b0097b3d769c43961d15f51d47f9c8 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 20:04:45 +0800 Subject: [PATCH 123/127] refactor handler paths --- .../main/scala/hkmc2/codegen/HandlerLowering.scala | 12 +++++++----- .../src/main/scala/hkmc2/codegen/Lifter.scala | 10 +++++----- .../src/main/scala/hkmc2/codegen/Lowering.scala | 13 ++++++------- .../scala/hkmc2/codegen/StackSafeTransform.scala | 5 ++--- .../main/scala/hkmc2/codegen/UsedVarAnalyzer.scala | 7 +++---- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index 5eb042ff1..84a0a5b19 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -63,8 +63,12 @@ class HandlerPaths(using Elaborator.State): val retClsSym: ClassSymbol = State.returnClsSymbol val mkEffectPath: Path = runtimePath.selN(Tree.Ident("mkEffect")) val handleBlockImplPath: Path = runtimePath.selN(Tree.Ident("handleBlockImpl")) + val stackDelayClsPath: Path = runtimePath.selN(Tree.Ident("StackDelay")) + + def isHandlerClsPath(p: Path) = + (p eq contClsPath) || (p eq stackDelayClsPath) || (p eq effectSigPath) || (p eq retClsPath) -class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): +class HandlerLowering(paths: HandlerPaths)(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def funcLikeHandlerCtx(ctorThis: Option[Path], isHandlerMtd: Bool, nme: Str) = HandlerCtx(!isHandlerMtd, false, isHandlerMtd, false, nme, ctorThis, state => @@ -80,8 +84,6 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): private def handlerMtdCtx(nme: Str) = funcLikeHandlerCtx(N, true, nme) private def handlerCtx(using HandlerCtx): HandlerCtx = summon - private val paths = new HandlerPaths - private def freshTmp(dbgNme: Str = "tmp") = new TempSymbol(N, dbgNme) private def rtThrowMsg(msg: Str) = Throw( @@ -574,6 +576,6 @@ class HandlerLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx): transform.applyBlock(b) - def translateTopLevel(b: Block): (Block, HandlerPaths) = - (translateBlock(b, topLevelCtx(s"Cont$$topLevel$$BAD")), paths) + def translateTopLevel(b: Block): Block = + translateBlock(b, topLevelCtx(s"Cont$$topLevel$$BAD")) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 12bf96ea1..2a4fccb4e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -208,6 +208,10 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): object LifterCtx: def empty = LifterCtx() def withLocals(u: UsedLocalsMap) = empty.copy(usedLocals = u) + + def isHandlerClsPath(p: Path) = handlerPaths match + case None => false + case Some(paths) => paths.isHandlerClsPath(p) /** * Creates a capture class for a function consisting of its mutable (and possibly immutable) local variables. @@ -396,23 +400,19 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): own.mapConserve(_.subst) isym.subst sym.subst - // `parentPath` is currently not checked, as checking it as shown below breaks lowering continuation classes - // and other handler-related classes. - /* // Check if `extends` is a complex expression, i.e. not just extending a class parentPath match case None => () + case Some(path) if isHandlerClsPath(path) => () case Some(Select(RefOfBms(s), Tree.Ident("class"))) if !ignored.contains(s) => () case Some(RefOfBms(s)) if !ignored.contains(s) => () case _ if !ignored.contains(defn.sym) => - println(parentPath) raise(WarningReport( msg"Cannot yet lift class/module `${sym.nme}` as it extends a first-class class or an expression." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += defn.sym case _ => () - */ paramsOpt.map(applyParamList) auxParams.map(applyParamList) methods.map(applyFunDefn) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 3a2e95da5..b9a654b84 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -586,18 +586,17 @@ class Lowering()(using Config, TL, Raise, State, Ctx): def topLevel(t: st): Block = val res = LambdaRewriter.desugar(term(t)(ImplctRet)(using Subst.empty)) + val handlerPaths = new HandlerPaths val stackSafe = config.stackSafety match case N => res - case S(sts) => StackSafeTransform(sts.stackLimit).transformTopLevel(res) - val (withHandlers, handlerPaths) = - if lowerHandlers then - val (b, paths) = HandlerLowering().translateTopLevel(stackSafe) - (b, S(paths)) - else (stackSafe, N) + case S(sts) => StackSafeTransform(sts.stackLimit, handlerPaths).transformTopLevel(res) + val withHandlers = if lowerHandlers + then HandlerLowering(handlerPaths).translateTopLevel(stackSafe) + else stackSafe val flattened = withHandlers.flattened val lifted = - if lift then Lifter(handlerPaths).transform(flattened) + if lift then Lifter(S(handlerPaths)).transform(flattened) else flattened MergeMatchArmTransformer.applyBlock(lifted) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index b3d06ef29..2eb6bb1c4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -8,7 +8,7 @@ import hkmc2.semantics.Elaborator.State import hkmc2.semantics.* import hkmc2.syntax.Tree -class StackSafeTransform(depthLimit: Int)(using State): +class StackSafeTransform(depthLimit: Int, paths: HandlerPaths)(using State): private val STACK_LIMIT_IDENT: Tree.Ident = Tree.Ident("stackLimit") private val STACK_DEPTH_IDENT: Tree.Ident = Tree.Ident("stackDepth") private val STACK_OFFSET_IDENT: Tree.Ident = Tree.Ident("stackOffset") @@ -17,7 +17,6 @@ class StackSafeTransform(depthLimit: Int)(using State): private val runtimePath: Path = State.runtimeSymbol.asPath private val checkDepthPath: Path = runtimePath.selN(Tree.Ident("checkDepth")) private val resetDepthPath: Path = runtimePath.selN(Tree.Ident("resetDepth")) - private val stackDelayClsPath: Path = runtimePath.selN(Tree.Ident("StackDelay")) private val stackLimitPath: Path = runtimePath.selN(STACK_LIMIT_IDENT) private val stackDepthPath: Path = runtimePath.selN(STACK_DEPTH_IDENT) private val stackOffsetPath: Path = runtimePath.selN(STACK_OFFSET_IDENT) @@ -56,7 +55,7 @@ class StackSafeTransform(depthLimit: Int)(using State): // the global stack handler is created here HandleBlock( handlerSym, resSym, - stackDelayClsPath, Nil, clsSym, + paths.stackDelayClsPath, Nil, clsSym, Handler( BlockMemberSymbol("perform", Nil), resumeSym, ParamList(ParamListFlags.empty, Nil, N) :: Nil, /* diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 813ea7fb8..dd2e405c8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -113,10 +113,9 @@ class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): case S(c: ClsLikeDefn) => c.k is syntax.Mod case _ => false - def isContClassPth(p: Path) = handlerPaths match + def isHandlerClsPath(p: Path) = handlerPaths match case None => false - case Some(paths) => paths.contClsPath eq p - + case Some(paths) => paths.isHandlerClsPath(p) private val blkMutCache: MutMap[Local, AccessInfo] = MutMap.empty private def blkAccessesShallow(b: Block, cacheId: Opt[Local] = N): AccessInfo = @@ -328,7 +327,7 @@ class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): // special case continuation classes defn match case c: ClsLikeDefn => c.parentPath match - case S(path) if isContClassPth(path) => return + case S(path) if isHandlerClsPath(path) => return // treat the continuation class as if it does not exist case _ => () case _ => () From 9a002c77035a7c567550aff58b9c80d1fac12ec2 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 20:45:00 +0800 Subject: [PATCH 124/127] don't lift unliftable classes --- .../src/main/scala/hkmc2/codegen/Lifter.scala | 42 ++++++++++++++++--- .../src/test/mlscript/lifter/ClassInFun.mls | 3 +- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 2a4fccb4e..d6e5f5f76 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -320,9 +320,11 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): // returns (ignored classes, modules, objects) def createMetadata(d: Defn, ctx: LifterCtx): (Set[BlockMemberSymbol], List[ClsLikeDefn], List[ClsLikeDefn]) = var ignored: 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 @@ -364,7 +366,8 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): val inModTopLevel = if isMod then inModuleDefns(d) else Set.empty ignored ++= inModTopLevel - + + // search for unliftable classes and build the extends graph val clsSyms = clsSymToBms.values.toSet new BlockTraverser(SymbolSubst()): applyDefn(d) @@ -378,6 +381,7 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): N, Diagnostic.Source.Compilation )) ignored += value + unliftable += value case _ => () case _ => () @@ -400,18 +404,23 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): own.mapConserve(_.subst) isym.subst sym.subst - // Check if `extends` is a complex expression, i.e. not just extending a class + // Check if `extends` is a complex expression, i.e. not just extending a class. + // If it's just a class, add it to an graph where edges are class extensions. + // If B extends A, then A -> B is an edge parentPath match case None => () case Some(path) if isHandlerClsPath(path) => () - case Some(Select(RefOfBms(s), Tree.Ident("class"))) if !ignored.contains(s) => () - case Some(RefOfBms(s)) if !ignored.contains(s) => () + 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) => raise(WarningReport( - msg"Cannot yet lift class/module `${sym.nme}` as it extends a first-class class or an expression." -> N :: Nil, + msg"Cannot yet lift class/module `${sym.nme}` as it extends an expression." -> N :: Nil, N, Diagnostic.Source.Compilation )) ignored += defn.sym + unliftable += defn.sym case _ => () paramsOpt.map(applyParamList) auxParams.map(applyParamList) @@ -428,9 +437,30 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): N, Diagnostic.Source.Compilation )) ignored += l + unliftable += l case _ => super.applyValue(v) - (ignored, modules, objects) + // analyze the extends graph + val extendsEdges = extendsGraph.groupBy(_._1).map: + case (a, bs) => a -> bs.map(_._2) + .toMap + var newUnliftable: Set[BlockMemberSymbol] = Set.empty + // dfs starting from unliftable classes + def dfs(s: BlockMemberSymbol): Unit = + for + edges <- extendsEdges.get(s) + b <- edges if !newUnliftable.contains(b) && !ignored.contains(b) + do + raise(WarningReport( + msg"Cannot yet lift class/module `${b.nme}` as it extends an unliftable class." -> N :: Nil, + N, Diagnostic.Source.Compilation + )) + newUnliftable += b + dfs(b) + for s <- ignored do + dfs(s) + + (ignored ++ newUnliftable, modules, objects) extension (b: Block) private def floatOut(ctx: LifterCtx) = diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index f9723e7dc..176473694 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -727,4 +727,5 @@ fun test(x) = B().get test(2) //│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) +//│ ═══[WARNING] Cannot yet lift class/module `B` as it extends an unliftable class. +//│ = 2 From 5148cae92a30905629a42a3873dd15d6f9f7d69e Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 20:50:44 +0800 Subject: [PATCH 125/127] Update tests --- .../src/test/mlscript-compile/Predef.mjs | 16 ++++---- .../src/test/mlscript-compile/Runtime.mjs | 8 ++-- .../src/test/mlscript-compile/Stack.mjs | 4 +- .../test/mlscript-compile/apps/Accounting.mjs | 40 +++++++++---------- .../src/test/mlscript/backlog/Lifter.mls | 3 +- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index 131391e75..777d574dd 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -195,9 +195,9 @@ Predef1 = class Predef { } else if (arg1 === null) { return "null" } else if (arg1 instanceof globalThis.Array) { - lambda = function lambda(arg11, arg2) { + lambda = (undefined, function (arg11, arg2) { return arg11 + arg2 - }; + }); tmp = Predef.fold(lambda); tmp1 = Predef.interleave(", "); tmp2 = Predef.map(Predef.render); @@ -207,9 +207,9 @@ Predef1 = class Predef { } else if (typeof arg1 === 'string') { return runtime.safeCall(globalThis.JSON.stringify(arg1)) } else if (arg1 instanceof globalThis.Set) { - lambda1 = function lambda(arg11, arg2) { + lambda1 = (undefined, function (arg11, arg2) { return arg11 + arg2 - }; + }); tmp5 = Predef.fold(lambda1); tmp6 = Predef.interleave(", "); tmp7 = Predef.map(Predef.render); @@ -217,9 +217,9 @@ Predef1 = class Predef { tmp9 = runtime.safeCall(tmp6(...tmp8)); return runtime.safeCall(tmp5("Set{", ...tmp9, "}")) } else if (arg1 instanceof globalThis.Map) { - lambda2 = function lambda(arg11, arg2) { + lambda2 = (undefined, function (arg11, arg2) { return arg11 + arg2 - }; + }); tmp10 = Predef.fold(lambda2); tmp11 = Predef.interleave(", "); tmp12 = Predef.map(Predef.render); @@ -377,9 +377,9 @@ Predef1 = class Predef { tmp4 = ""; } name = tmp4; - lambda = function lambda(arg11, arg2) { + lambda = (undefined, function (arg11, arg2) { return arg11 + arg2 - }; + }); tmp5 = Predef.fold(lambda); if (isUB === true) { tmp6 = ""; diff --git a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs index b8ca107ff..550f8cdf0 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs @@ -100,7 +100,7 @@ Runtime1 = class Runtime { tmp = cont.constructor.name + "(pc="; tmp1 = tmp + cont.pc; result = tmp1; - lambda = function lambda(m, marker) { + lambda = (undefined, function (m, marker) { let scrut4, tmp12, tmp13; scrut4 = runtime.safeCall(m.has(cont)); if (scrut4 === true) { @@ -111,7 +111,7 @@ Runtime1 = class Runtime { } else { return runtime.Unit } - }; + }); tmp2 = lambda; tmp3 = runtime.safeCall(hl.forEach(tmp2)); scrut1 = runtime.safeCall(vis.has(cont)); @@ -154,7 +154,7 @@ Runtime1 = class Runtime { let scrut, result, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, lambda; if (cont1 instanceof Runtime.HandlerContFrame.class) { result = cont1.handler.constructor.name; - lambda = function lambda(m, marker) { + lambda = (undefined, function (m, marker) { let scrut3, tmp8, tmp9; scrut3 = runtime.safeCall(m.has(cont1)); if (scrut3 === true) { @@ -165,7 +165,7 @@ Runtime1 = class Runtime { } else { return runtime.Unit } - }; + }); tmp = lambda; tmp1 = runtime.safeCall(hl1.forEach(tmp)); scrut1 = runtime.safeCall(vis1.has(cont1)); diff --git a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs index b52b8c217..1bba7cf7f 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Stack.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Stack.mjs @@ -92,7 +92,7 @@ Stack1 = class Stack { let go, tmp, tmp1; go = function go(heads, tails) { let lambda; - lambda = function lambda(caseScrut) { + lambda = (undefined, function (caseScrut) { let param0, param1, h, t, param01, param11, h2, t2, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; if (caseScrut instanceof Stack.Cons.class) { param0 = caseScrut.head; @@ -133,7 +133,7 @@ Stack1 = class Stack { } else { throw new globalThis.Error("match error"); } - }; + }); return lambda }; tmp = go(Stack.Nil, Stack.Nil); diff --git a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs index a882d2656..759f75744 100644 --- a/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/apps/Accounting.mjs @@ -72,9 +72,9 @@ Accounting1 = class Accounting { tmp = this.wln(""); tmp1 = Str.concat2("|", "Year"); tmp2 = Str.concat2(tmp1, "|"); - lambda = function lambda(x) { + lambda = (undefined, function (x) { return x.name - }; + }); tmp3 = runtime.safeCall(this$Accounting.lines.map(lambda)); tmp4 = runtime.safeCall(tmp3.join("|")); tmp5 = Str.concat2(tmp2, tmp4); @@ -82,9 +82,9 @@ Accounting1 = class Accounting { tmp7 = this.wln(tmp6); tmp8 = Str.concat2("|", "---"); tmp9 = Str.concat2(tmp8, "|"); - lambda1 = function lambda(x) { + lambda1 = (undefined, function (x) { return "--:" - }; + }); tmp10 = runtime.safeCall(this$Accounting.lines.map(lambda1)); tmp11 = runtime.safeCall(tmp10.join("|")); tmp12 = Str.concat2(tmp9, tmp11); @@ -96,9 +96,9 @@ Accounting1 = class Accounting { tmp = runtime.safeCall(globalThis.String(label)); tmp1 = Str.concat2("|", tmp); tmp2 = Str.concat2(tmp1, "|"); - lambda = function lambda(x) { + lambda = (undefined, function (x) { return this$Accounting.display(x.balance) - }; + }); tmp3 = runtime.safeCall(this$Accounting.lines.map(lambda)); tmp4 = runtime.safeCall(tmp3.join("|")); tmp5 = Str.concat2(tmp2, tmp4); @@ -109,11 +109,11 @@ Accounting1 = class Accounting { let tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, lambda, lambda1, lambda2, lambda3, lambda4, lambda5, lambda6; tmp = this.wln(""); const this$Report = this; - lambda = function lambda(x) { + lambda = (undefined, function (x) { let tmp27; tmp27 = this$Report.wln(x); return this$Report.wln("") - }; + }); tmp1 = runtime.safeCall(this$Accounting.warnings.forEach(lambda)); tmp2 = this.wln("### Remaining Available Funds"); tmp3 = this.wln(""); @@ -125,17 +125,17 @@ Accounting1 = class Accounting { tmp9 = this.wln(tmp8); tmp10 = Str.concat2("|", "Matchable"); tmp11 = Str.concat2(tmp10, "|"); - lambda1 = function lambda(x) { + lambda1 = (undefined, function (x) { return x.isMatchable - }; + }); tmp12 = runtime.safeCall(this$Accounting.lines.filter(lambda1)); - lambda2 = function lambda(x) { + lambda2 = (undefined, function (x) { return x.balance - }; + }); tmp13 = runtime.safeCall(tmp12.map(lambda2)); - lambda3 = function lambda(a, b) { + lambda3 = (undefined, function (a, b) { return a + b - }; + }); tmp14 = tmp13.reduce(lambda3, 0); tmp15 = this$Accounting.display(tmp14); tmp16 = Str.concat2(tmp11, tmp15); @@ -143,17 +143,17 @@ Accounting1 = class Accounting { tmp18 = this.wln(tmp17); tmp19 = Str.concat2("|", "Non-matchable"); tmp20 = Str.concat2(tmp19, "|"); - lambda4 = function lambda(x) { + lambda4 = (undefined, function (x) { return Predef.not(x.isMatchable) - }; + }); tmp21 = runtime.safeCall(this$Accounting.lines.filter(lambda4)); - lambda5 = function lambda(x) { + lambda5 = (undefined, function (x) { return x.balance - }; + }); tmp22 = runtime.safeCall(tmp21.map(lambda5)); - lambda6 = function lambda(a, b) { + lambda6 = (undefined, function (a, b) { return a + b - }; + }); tmp23 = tmp22.reduce(lambda6, 0); tmp24 = this$Accounting.display(tmp23); tmp25 = Str.concat2(tmp20, tmp24); diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index 11c23d872..b65480f5a 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -121,7 +121,8 @@ fun test(x) = B().get test(2) //│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:A (class hkmc2.semantics.BlockMemberSymbol) +//│ ═══[WARNING] Cannot yet lift class/module `B` as it extends an unliftable class. +//│ = 2 /// The following are related to modules and objects. /// From b0055f359d26824e3d147ccc78208811e7d9b2bd Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 22:05:59 +0800 Subject: [PATCH 126/127] dedupe tests --- .../src/test/mlscript/backlog/Lifter.mls | 2 +- .../src/test/mlscript/lifter/ClassInFun.mls | 207 ++++++------------ .../src/test/mlscript/lifter/DefnsInClass.mls | 84 +------ .../src/test/mlscript/lifter/FunInFun.mls | 105 +++------ .../test/mlscript/lifter/ModulesObjects.mls | 149 ++++--------- 5 files changed, 158 insertions(+), 389 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls index b65480f5a..c540669fe 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/Lifter.mls @@ -111,7 +111,7 @@ foo(0, 2) //│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 //│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' -// B is lifted but A is not, and since B extends A, it cannot access A's BlockMemberSymbol. +// Since B extends A and A is not lifted, B cannot access A's BlockMemberSymbol. We can't lift B. :todo fun test(x) = class A with diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 176473694..291b1eea9 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -307,23 +307,6 @@ f(1, 2).get() //│ runtime.safeCall(tmp2.get()) //│ = 1 -:todo -:w -:expect 1 -fun f(used1, unused1) = - fun g(g_arg) = - let used3 = 2 - fun h = used3 - used1 + h - let unused2 = 2 - class Test(a) with - fun get() = used1 - 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 - :expect 1 fun f(used1, unused1) = fun g(g_arg) = @@ -350,42 +333,42 @@ fun f(used1, unused1) = new Test f(1, 2).get() //│ JS (unsanitized): -//│ let h3, Test8, g3, f5, tmp5, Test$ctor3, Test$3, g$3, h$3; -//│ h$3 = function h$(used3) { +//│ let h2, Test7, g2, f4, tmp4, Test$ctor3, Test$3, g$2, h$2; +//│ h$2 = function h$(used3) { //│ return used3 //│ }; -//│ h3 = function h(used3) { +//│ h2 = function h(used3) { //│ return () => { -//│ return h$3(used3) +//│ return h$2(used3) //│ } //│ }; -//│ g$3 = function g$(used1, g_arg) { -//│ let used3, tmp6; +//│ g$2 = function g$(used1, g_arg) { +//│ let used3, tmp5; //│ used3 = 2; -//│ tmp6 = h$3(used3); -//│ return used1 + tmp6 +//│ tmp5 = h$2(used3); +//│ return used1 + tmp5 //│ }; -//│ g3 = function g(used1) { +//│ g2 = function g(used1) { //│ return (g_arg) => { -//│ return g$3(used1, g_arg) +//│ return g$2(used1, g_arg) //│ } //│ }; //│ Test$3 = function Test$(used1$0) { -//│ let tmp6; -//│ tmp6 = new Test8.class(); -//│ return tmp6(used1$0) +//│ let tmp5; +//│ tmp5 = new Test7.class(); +//│ return tmp5(used1$0) //│ }; //│ Test$ctor3 = function Test$ctor(used1$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new Test8.class(); -//│ return tmp6(used1$0) +//│ let tmp5; +//│ tmp5 = new Test7.class(); +//│ return tmp5(used1$0) //│ } //│ }; -//│ Test8 = function Test(used1$01) { +//│ Test7 = function Test(used1$01) { //│ return new Test.class()(used1$01); //│ }; -//│ Test8.class = class Test7 { +//│ Test7.class = class Test6 { //│ constructor() { //│ return (used1$0) => { //│ this.used1$0 = used1$0; @@ -397,13 +380,13 @@ f(1, 2).get() //│ } //│ toString() { return "Test"; } //│ }; -//│ f5 = function f(used1, unused1) { +//│ f4 = function f(used1, unused1) { //│ let unused2; //│ unused2 = 2; //│ return Test$3(used1) //│ }; -//│ tmp5 = f5(1, 2); -//│ runtime.safeCall(tmp5.get()) +//│ tmp4 = f4(1, 2); +//│ runtime.safeCall(tmp4.get()) //│ = 1 :sjs @@ -415,17 +398,17 @@ fun f(x) = x f(1) //│ JS (unsanitized): -//│ let A1, f6, A$ctor, A$, f$capture1; +//│ let A1, f5, A$ctor, A$, f$capture1; //│ A$ = function A$(f$capture$0) { -//│ let tmp6; -//│ tmp6 = new A1.class(); -//│ return tmp6(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1.class(); +//│ return tmp5(f$capture$0) //│ }; //│ A$ctor = function A$ctor(f$capture$0) { //│ return () => { -//│ let tmp6; -//│ tmp6 = new A1.class(); -//│ return tmp6(f$capture$0) +//│ let tmp5; +//│ tmp5 = new A1.class(); +//│ return tmp5(f$capture$0) //│ } //│ }; //│ A1 = function A() { @@ -455,14 +438,14 @@ f(1) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f6 = function f(x) { -//│ let tmp6, tmp7, capture; +//│ f5 = function f(x) { +//│ let tmp5, tmp6, capture; //│ capture = new f$capture1(x); -//│ tmp6 = A$(capture); -//│ tmp7 = runtime.safeCall(tmp6.f()); +//│ tmp5 = A$(capture); +//│ tmp6 = runtime.safeCall(tmp5.f()); //│ return capture.x0$ //│ }; -//│ f6(1) +//│ f5(1) //│ = 2 // only w should be in a capture @@ -484,17 +467,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f7, tmp6, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; +//│ let Bad1, Good1, f6, tmp5, Bad$ctor, Bad$, Good$ctor, Good$, f$capture3; //│ Good$ = function Good$(x$1, y$2, z$3, f$capture$0) { -//│ let tmp7; -//│ tmp7 = new Good1.class(); -//│ return tmp7(x$1, y$2, z$3, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1.class(); +//│ return tmp6(x$1, y$2, z$3, f$capture$0) //│ }; //│ Good$ctor = function Good$ctor(x$1, y$2, z$3, f$capture$0) { //│ return () => { -//│ let tmp7; -//│ tmp7 = new Good1.class(); -//│ return tmp7(x$1, y$2, z$3, f$capture$0) +//│ let tmp6; +//│ tmp6 = new Good1.class(); +//│ return tmp6(x$1, y$2, z$3, f$capture$0) //│ } //│ }; //│ Good1 = function Good() { @@ -513,24 +496,24 @@ f().foo() //│ } //│ } //│ foo() { -//│ let tmp7, tmp8; +//│ let tmp6, tmp7; //│ this.z$3 = 100; -//│ tmp7 = this.x$1 + this.y$2; -//│ tmp8 = tmp7 + this.z$3; -//│ return tmp8 + this.f$capture$0.w0$ +//│ tmp6 = this.x$1 + this.y$2; +//│ tmp7 = tmp6 + this.z$3; +//│ return tmp7 + this.f$capture$0.w0$ //│ } //│ toString() { return "Good(" + "" + ")"; } //│ }; //│ Bad$ = function Bad$(f$capture$0) { -//│ let tmp7; -//│ tmp7 = new Bad1.class(); -//│ return tmp7(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1.class(); +//│ return tmp6(f$capture$0) //│ }; //│ Bad$ctor = function Bad$ctor(f$capture$0) { //│ return () => { -//│ let tmp7; -//│ tmp7 = new Bad1.class(); -//│ return tmp7(f$capture$0) +//│ let tmp6; +//│ tmp6 = new Bad1.class(); +//│ return tmp6(f$capture$0) //│ } //│ }; //│ Bad1 = function Bad() { @@ -560,19 +543,19 @@ f().foo() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.w0$) + ")"; } //│ }; -//│ f7 = function f() { -//│ let x, y, z, tmp7, tmp8, capture; +//│ f6 = function f() { +//│ let x, y, z, tmp6, tmp7, capture; //│ capture = new f$capture3(null); //│ x = 1; //│ y = 10; //│ z = 10; //│ capture.w0$ = 1000; -//│ tmp7 = Bad$(capture); -//│ tmp8 = runtime.safeCall(tmp7.foo()); +//│ tmp6 = Bad$(capture); +//│ tmp7 = runtime.safeCall(tmp6.foo()); //│ return Good$(x, y, z, capture) //│ }; -//│ tmp6 = f7(); -//│ runtime.safeCall(tmp6.foo()) +//│ tmp5 = f6(); +//│ runtime.safeCall(tmp5.foo()) //│ = 10111 :fixme @@ -617,27 +600,6 @@ sum(100) // instance checks -:todo -:w -fun foo(x, n) = - class C() - let res = if x is C then "Y" else "N" - if n > 0 then res + foo(C(), n - 1) else "" -//│ ═══[WARNING] Cannot yet lift class/module `C` as it is used in an instance check. - -:todo -:w -fun foo() = - class C() - C -//│ ═══[WARNING] Cannot yet lift class `C` as it is used as a first-class class. - -:todo -:expect "NN" -foo(0, 2) -//│ ═══[RUNTIME ERROR] Error: Function 'foo' expected 0 arguments but got 2 -//│ ═══[RUNTIME ERROR] Expected: '"NN"', got: 'undefined' - :w fun f() = class Test(a) with @@ -661,53 +623,35 @@ fun f(x) = f(2).get() //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ JS (unsanitized): -//│ let h4, f10, tmp13, h$4; -//│ h$4 = function h$(Test$instance, x) { +//│ let h3, f9, tmp12, h$3; +//│ h$3 = function h$(Test$instance, x) { //│ return x //│ }; -//│ h4 = function h(Test$instance, x) { +//│ h3 = function h(Test$instance, x) { //│ return () => { -//│ return h$4(Test$instance, x) +//│ return h$3(Test$instance, x) //│ } //│ }; -//│ f10 = function f(x) { -//│ let Test11, foo3; -//│ Test11 = function Test() { +//│ f9 = function f(x) { +//│ let Test10, foo1; +//│ Test10 = function Test() { //│ return new Test.class(); //│ }; -//│ Test11.class = class Test10 { +//│ Test10.class = class Test9 { //│ constructor() {} //│ get() { -//│ return h$4(this, x) +//│ return h$3(this, x) //│ } //│ toString() { return "Test(" + "" + ")"; } //│ }; -//│ foo3 = Test11; -//│ return runtime.safeCall(foo3()) +//│ foo1 = Test10; +//│ return runtime.safeCall(foo1()) //│ }; -//│ tmp13 = f10(2); -//│ runtime.safeCall(tmp13.get()) +//│ tmp12 = f9(2); +//│ runtime.safeCall(tmp12.get()) //│ ═══[WARNING] Cannot yet lift class `Test` as it is used as a first-class class. //│ = 2 -:fixme -class Test -fun hello() = - class Test2 extends Test - 2 -//│ ═══[RUNTIME ERROR] TypeError: Class extends value undefined is not a constructor or null - -:fixme -:expect 2 -fun test(x) = - class A with - fun get = x - class B() extends A - B().get -test(2) -//│ ═══[RUNTIME ERROR] Error: Access to required field 'get' yielded 'undefined' -//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' - :expect 2 fun test() = class A with @@ -716,16 +660,3 @@ fun test() = B().get test() //│ = 2 - -// will be easier with when `let class` is added -:todo -fun test(x) = - class A with - fun get = x - class B() extends A - 0 is A - B().get -test(2) -//│ ═══[WARNING] Cannot yet lift class/module `A` as it is used in an instance check. -//│ ═══[WARNING] Cannot yet lift class/module `B` as it extends an unliftable class. -//│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 20cf70157..c471ecefa 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -58,87 +58,22 @@ A(1).getA() //│ = 3 :sjs -class A(x) with - class B(y) with - fun getB() = x + y - fun getA() = this.B(2).getB() -//│ JS (unsanitized): -//│ let B3, A3, B$ctor1, B$1; -//│ B$1 = function B$(A$instance$0, y) { -//│ let tmp1; -//│ tmp1 = new B3.class(y); -//│ return tmp1(A$instance$0) -//│ }; -//│ B$ctor1 = function B$ctor(A$instance$0) { -//│ return (y) => { -//│ let tmp1; -//│ tmp1 = new B3.class(y); -//│ return tmp1(A$instance$0) -//│ } -//│ }; -//│ B3 = function B(y1) { -//│ return (A$instance$01) => { -//│ return new B.class(y1)(A$instance$01); -//│ } -//│ }; -//│ B3.class = class B2 { -//│ constructor(y) { -//│ return (A$instance$0) => { -//│ this.y = y; -//│ this.A$instance$0 = A$instance$0; -//│ return this; -//│ } -//│ } -//│ getB() { -//│ return this.A$instance$0.x + this.y -//│ } -//│ toString() { return "B(" + globalThis.Predef.render(this.y) + ")"; } -//│ }; -//│ A3 = function A(x1) { -//│ return new A.class(x1); -//│ }; -//│ A3.class = class A2 { -//│ constructor(x) { -//│ this.x = x; -//│ } -//│ getA() { -//│ let tmp1; -//│ tmp1 = runtime.safeCall(this.B(2)); -//│ return runtime.safeCall(tmp1.getB()) -//│ } -//│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } -//│ }; - -// I use the optional `symbol` parameter of `Select` to detect references to the -// BlockMemberSymbol. But when this symbol is not present, there is no way to properly -// detect it. What do to? -:fixme -:expect 3 -A(1).getA() -//│ ═══[RUNTIME ERROR] TypeError: this.B is not a function -//│ ═══[RUNTIME ERROR] Expected: '3', got: 'undefined' - -:ssjs class A with val x = fun g() = 2 g (new A).x() -//│ JS: -//│ g$ = function g$(...args) { -//│ globalThis.Predef.checkArgs("g$", 1, true, args.length); -//│ let A$instance = args[0]; +//│ JS (unsanitized): +//│ let g, A3, tmp1, g$; +//│ g$ = function g$(A$instance) { //│ return 2 //│ }; -//│ g = function g(...args) { -//│ globalThis.Predef.checkArgs("g", 1, true, args.length); -//│ let A$instance = args[0]; -//│ return (...args1) => { -//│ globalThis.Predef.checkArgs("", 0, true, args1.length); -//│ return runtime.checkCall(g$(A$instance)) +//│ g = function g(A$instance) { +//│ return () => { +//│ return g$(A$instance) //│ } //│ }; -//│ A5 = class A4 { +//│ A3 = class A2 { //│ constructor() { //│ let g$this; //│ g$this = runtime.safeCall(g(this)); @@ -146,7 +81,6 @@ class A with //│ } //│ toString() { return "A"; } //│ }; -//│ tmp2 = new A5(); -//│ block$res5 = runtime.safeCall(tmp2.x()); -//│ undefined +//│ tmp1 = new A3(); +//│ runtime.safeCall(tmp1.x()) //│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 495f172ea..feb3a692e 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -116,30 +116,6 @@ f(1, 2) //│ f3(1, 2) //│ = 5 -// don't touch g if not needed -:todo -:sjs -fun f(used1, unused1) = - let used2 = unused1 - fun g(g_arg) = g_arg + 1 - let unused2 = 2 - g(used2) + unused2 -f(1, 2) -//│ JS (unsanitized): -//│ let g4, f4; -//│ g4 = function g(g_arg) { -//│ return g_arg + 1 -//│ }; -//│ f4 = function f(used1, unused1) { -//│ let used2, unused2, tmp; -//│ used2 = unused1; -//│ unused2 = 2; -//│ tmp = g4(used2); -//│ return tmp + unused2 -//│ }; -//│ f4(1, 2) -//│ = 5 - // preserve order :sjs fun f(a1, a2, a3, a4, a5, a6) = @@ -147,7 +123,7 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let g5, f5, g$4; +//│ let g4, f4, g$4; //│ g$4 = function g$(a1, a2, a3, a4, a5, a6) { //│ let tmp, tmp1, tmp2, tmp3; //│ tmp = a1 + a2; @@ -156,17 +132,17 @@ f(1,2,3,4,5,6) //│ tmp3 = tmp2 + a5; //│ return tmp3 + a6 //│ }; -//│ g5 = function g(a1, a2, a3, a4, a5, a6) { +//│ g4 = function g(a1, a2, a3, a4, a5, a6) { //│ return () => { //│ return g$4(a1, a2, a3, a4, a5, a6) //│ } //│ }; -//│ f5 = function f(a1, a2, a3, a4, a5, a6) { +//│ f4 = function f(a1, a2, a3, a4, a5, a6) { //│ let tmp; //│ tmp = g$4(a1, a2, a3, a4, a5, a6); //│ return tmp //│ }; -//│ f5(1, 2, 3, 4, 5, 6) +//│ f4(1, 2, 3, 4, 5, 6) //│ = 21 :expect "01" @@ -185,12 +161,12 @@ f1() let y = f2().toString() x + y //│ JS (unsanitized): -//│ let g6, h, f6, Tuple1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; +//│ let g5, h, f5, Tuple1, ret, f11, f21, x, y, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, h$, g$5, f$capture1; //│ g$5 = function g$(f$capture2) { //│ f$capture2.a0$ = 1; //│ return runtime.Unit //│ }; -//│ g6 = function g(f$capture2) { +//│ g5 = function g(f$capture2) { //│ return () => { //│ return g$5(f$capture2) //│ } @@ -212,11 +188,11 @@ x + y //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.a0$) + ")"; } //│ }; -//│ f6 = function f() { +//│ f5 = function f() { //│ let capture, g$this, h$this; //│ capture = new f$capture1(null); //│ capture.a0$ = 0; -//│ g$this = runtime.safeCall(g6(capture)); +//│ g$this = runtime.safeCall(g5(capture)); //│ h$this = runtime.safeCall(h(capture)); //│ return Tuple1(g$this, h$this) //│ }; @@ -230,7 +206,7 @@ x + y //│ } //│ toString() { return "Tuple(" + globalThis.Predef.render(this.a) + ", " + globalThis.Predef.render(this.b) + ")"; } //│ }; -//│ tmp = f6(); +//│ tmp = f5(); //│ ret = tmp; //│ f11 = ret.a; //│ f21 = ret.b; @@ -276,7 +252,7 @@ let d = iFun() a.toString() + b + c + d //│ JS (unsanitized): //│ let ret1, gRet, g21, hFun, iFun, a, b, c, d, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; -//│ tmp6 = f7(1); +//│ tmp6 = f6(1); //│ ret1 = tmp6; //│ gRet = ret1.a; //│ g21 = ret1.b; @@ -318,12 +294,12 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let g7, h2, f8, h$2, g$6, f$capture5; +//│ let g6, h2, f7, h$2, g$6, f$capture5; //│ g$6 = function g$(immutable, f$capture6) { //│ f$capture6.mutated0$ = 2; //│ return immutable + f$capture6.mutated0$ //│ }; -//│ g7 = function g(immutable, f$capture6) { +//│ g6 = function g(immutable, f$capture6) { //│ return () => { //│ return g$6(immutable, f$capture6) //│ } @@ -345,7 +321,7 @@ f(1, 2, 1000) //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.mutated0$) + ")"; } //│ }; -//│ f8 = function f(unused, immutable, mutated) { +//│ f7 = function f(unused, immutable, mutated) { //│ let a1, tmp15, tmp16, tmp17, capture; //│ capture = new f$capture5(mutated); //│ tmp15 = g$6(immutable, capture); @@ -354,7 +330,7 @@ f(1, 2, 1000) //│ tmp17 = a1 + tmp16; //│ return tmp17 + unused //│ }; -//│ f8(1, 2, 1000) +//│ f7(1, 2, 1000) //│ = 7 // if used as a higher order function, pass closures @@ -385,7 +361,7 @@ fun f(arg) = h(5) f(2) //│ JS (unsanitized): -//│ let g10, h3, f12, h$3, g$9; +//│ let g9, h3, f10, h$3, g$9; //│ g$9 = function g$(arg, n) { //│ let scrut, tmp15; //│ scrut = n <= 0; @@ -396,7 +372,7 @@ f(2) //│ return h$3(arg, tmp15) //│ } //│ }; -//│ g10 = function g(arg) { +//│ g9 = function g(arg) { //│ return (n) => { //│ return g$9(arg, n) //│ } @@ -416,10 +392,10 @@ f(2) //│ return h$3(arg, n) //│ } //│ }; -//│ f12 = function f(arg) { +//│ f10 = function f(arg) { //│ return h$3(arg, 5) //│ }; -//│ f12(2) +//│ f10(2) //│ = 2 :expect 1 @@ -438,17 +414,17 @@ fun g() = h() g() //│ JS (unsanitized): -//│ let h4, f15, g13; -//│ f15 = function f() { +//│ let h4, f14, g12; +//│ f14 = function f() { //│ return 3 //│ }; //│ h4 = function h() { //│ return 3 //│ }; -//│ g13 = function g() { +//│ g12 = function g() { //│ return h4() //│ }; -//│ g13() +//│ g12() //│ = 3 :sjs @@ -467,7 +443,7 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let h5, f16, g14, tmp15, f$1, h$4, g$capture1; +//│ let h5, f15, g13, tmp15, f$1, h$4, g$capture1; //│ h$4 = function h$(x1, k, g$capture2) { //│ k = 5; //│ x1 = 4; @@ -484,7 +460,7 @@ g()(1) //│ g$capture2.y0$ = 2; //│ return x1 //│ }; -//│ f16 = function f(g$capture2) { +//│ f15 = function f(g$capture2) { //│ return (x1) => { //│ return f$1(g$capture2, x1) //│ } @@ -498,13 +474,13 @@ g()(1) //│ } //│ toString() { return "g$capture(" + globalThis.Predef.render(this.y0$) + ")"; } //│ }; -//│ g14 = function g() { +//│ g13 = function g() { //│ let capture; //│ capture = new g$capture1(null); //│ capture.y0$ = 0; -//│ return runtime.safeCall(f16(capture)) +//│ return runtime.safeCall(f15(capture)) //│ }; -//│ tmp15 = g14(); +//│ tmp15 = g13(); //│ runtime.safeCall(tmp15(1)) //│ = 1 @@ -547,7 +523,7 @@ fun f(x, cond) = set x = 3 () => x //│ JS (unsanitized): -//│ let f19, lambda, lambda1, lambda$, lambda$1; +//│ let f18, lambda, lambda1, lambda$, lambda$1; //│ lambda$1 = function lambda$(x1) { //│ return x1 //│ }; @@ -564,7 +540,7 @@ fun f(x, cond) = //│ return lambda$(x1) //│ } //│ }); -//│ f19 = function f(x1, cond) { +//│ f18 = function f(x1, cond) { //│ x1 = 1; //│ x1 = 2; //│ if (cond === true) { @@ -585,15 +561,15 @@ fun f() = x f() //│ JS (unsanitized): -//│ let f20, g17, f22, f$2, f$capture15; -//│ g17 = function g() { +//│ let f19, g16, f20, f$2, f$capture15; +//│ g16 = function g() { //│ return 1 //│ }; //│ f$2 = function f$(f$capture16) { //│ f$capture16.x0$ = 1; //│ return runtime.Unit //│ }; -//│ f20 = function f(f$capture16) { +//│ f19 = function f(f$capture16) { //│ return () => { //│ return f$2(f$capture16) //│ } @@ -607,14 +583,14 @@ f() //│ } //│ toString() { return "f$capture(" + globalThis.Predef.render(this.x0$) + ")"; } //│ }; -//│ f22 = function f() { +//│ f20 = function f() { //│ let tmp19, capture; //│ capture = new f$capture15(null); //│ capture.x0$ = 1; //│ tmp19 = f$2(capture); //│ return capture.x0$ //│ }; -//│ f22() +//│ f20() //│ = 1 let n = 0 @@ -628,16 +604,3 @@ fun f() = g() f() -:fixme -:expect [1] -:lift -fun f(x) = - fun g(...rest) = - print(x) - rest - let a = g - a(1) -f(2) -//│ > 2 -//│ ═══[RUNTIME ERROR] Expected: '[1]', got: '[]' -//│ = [] diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index 1a2a8ca3c..35fed0245 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -1,55 +1,6 @@ :lift :js -// the problem with all of these tests is that a Ref to a module's own ModuleSymbol -// (let's say M) emits `M` instead of `this` in the codegen - -:todo -fun foo(x, y) = - module M with - val test = 2 - fun foo() = - set y = 2 - x + y + test - M.foo() -//│ ═══[WARNING] Modules are not yet lifted. - -:todo -:expect 14 -foo(10, 0) -//│ = 14 - -:todo -fun foo(x, y) = - module M with - fun foo() = - set y = 2 - x + y - fun foo = M.foo() - foo -//│ ═══[WARNING] Modules are not yet lifted. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) - -:fixme -:expect 12 -foo(10, 0) -//│ ═══[RUNTIME ERROR] ReferenceError: foo2 is not defined -//│ ═══[RUNTIME ERROR] Expected: '12', got: 'undefined' - - -:todo -class A(x) with - module M with - fun getB() = x - fun getA() = M.getB() -//│ ═══[WARNING] Modules are not yet lifted. - -:todo -:expect 2 -A(2).getA() -//│ = 2 - - fun foo(x, y) = object M with val test = 2 @@ -71,19 +22,19 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //│ JS (unsanitized): -//│ let M6, foo31, foo4, foo3$, foo$capture1; -//│ foo3$ = function foo3$(M7, x, foo$capture2) { -//│ return M7.foo2() +//│ let M3, foo3, foo1, foo3$, foo$capture1; +//│ foo3$ = function foo3$(M4, x, foo$capture2) { +//│ return M4.foo2() //│ }; -//│ foo31 = function foo3(M7, x, foo$capture2) { +//│ foo3 = function foo3(M4, x, foo$capture2) { //│ return () => { -//│ return foo3$(M7, x, foo$capture2) +//│ return foo3$(M4, x, foo$capture2) //│ } //│ }; -//│ M6 = function M(x$11, foo$capture$01) { +//│ M3 = function M(x$11, foo$capture$01) { //│ return new M.class()(x$11, foo$capture$01); //│ }; -//│ M6.class = class M5 { +//│ M3.class = class M2 { //│ constructor() { //│ return (x$1, foo$capture$0) => { //│ this.x$1 = x$1; @@ -106,12 +57,12 @@ fun foo(x, y) = //│ } //│ toString() { return "foo$capture(" + globalThis.Predef.render(this.y0$) + ")"; } //│ }; -//│ foo4 = function foo(x, y) { -//│ let tmp1, M$, capture; +//│ foo1 = function foo(x, y) { +//│ let tmp, M$, capture; //│ capture = new foo$capture1(y); -//│ M$ = M6(x, capture); -//│ tmp1 = foo3$(M$, x, capture); -//│ return tmp1 +//│ M$ = M3(x, capture); +//│ tmp = foo3$(M$, x, capture); +//│ return tmp //│ }; :expect 12 @@ -125,11 +76,11 @@ class A(x) with fun getB() = x fun getA() = M.getB() //│ JS (unsanitized): -//│ let M8, A3; -//│ M8 = function M(A$instance$01) { +//│ let M5, A1; +//│ M5 = function M(A$instance$01) { //│ return new M.class()(A$instance$01); //│ }; -//│ M8.class = class M7 { +//│ M5.class = class M4 { //│ constructor() { //│ return (A$instance$0) => { //│ this.A$instance$0 = A$instance$0; @@ -141,13 +92,13 @@ class A(x) with //│ } //│ toString() { return "M"; } //│ }; -//│ A3 = function A(x1) { +//│ A1 = function A(x1) { //│ return new A.class(x1); //│ }; -//│ A3.class = class A2 { +//│ A1.class = class A { //│ constructor(x) { //│ this.x = x; -//│ this.M$ = M8(this); +//│ this.M$ = M5(this); //│ } //│ getA() { //│ return this.M$.getB() @@ -204,32 +155,32 @@ module M with val x = if A() is A then 2 else 3 M.A().get //│ JS (unsanitized): -//│ let M18, tmp4; -//│ M18 = class M17 { +//│ let M15, tmp3; +//│ M15 = class M14 { //│ static { -//│ let scrut, tmp5; +//│ let scrut, tmp4; //│ this.A = function A() { //│ return new A.class(); //│ }; -//│ this.A.class = class A7 { +//│ this.A.class = class A5 { //│ constructor() {} //│ get get() { -//│ return M18.x + M17.x; +//│ return M15.x + M14.x; //│ } //│ toString() { return "A(" + "" + ")"; } //│ }; -//│ scrut = M17.A(); -//│ if (scrut instanceof M17.A.class) { -//│ tmp5 = 2; +//│ scrut = M14.A(); +//│ if (scrut instanceof M14.A.class) { +//│ tmp4 = 2; //│ } else { -//│ tmp5 = 3; +//│ tmp4 = 3; //│ } -//│ this.x = tmp5; +//│ this.x = tmp4; //│ } //│ static toString() { return "M"; } //│ }; -//│ tmp4 = M18.A(); -//│ tmp4.get +//│ tmp3 = M15.A(); +//│ tmp3.get //│ = 4 module M with @@ -256,17 +207,17 @@ module M with 0 is A M.A(2).newB.newA_B(3).newB.get //│ JS (unsanitized): -//│ let B2, M22, tmp6, tmp7, B$ctor, B$; +//│ let B2, M19, tmp5, tmp6, B$ctor, B$; //│ B$ = function B$(A$instance$0) { -//│ let tmp8; -//│ tmp8 = new B2.class(); -//│ return tmp8(A$instance$0) +//│ let tmp7; +//│ tmp7 = new B2.class(); +//│ return tmp7(A$instance$0) //│ }; //│ B$ctor = function B$ctor(A$instance$0) { //│ return () => { -//│ let tmp8; -//│ tmp8 = new B2.class(); -//│ return tmp8(A$instance$0) +//│ let tmp7; +//│ tmp7 = new B2.class(); +//│ return tmp7(A$instance$0) //│ } //│ }; //│ B2 = function B() { @@ -285,22 +236,22 @@ M.A(2).newB.newA_B(3).newB.get //│ return this.A$instance$0.x; //│ } //│ newA_B(y) { -//│ return M22.A(y) +//│ return M19.A(y) //│ } //│ toString() { return "B(" + "" + ")"; } //│ }; -//│ M22 = class M21 { +//│ M19 = class M18 { //│ static { //│ let scrut; //│ this.A = function A(x1) { //│ return new A.class(x1); //│ }; -//│ this.A.class = class A9 { +//│ this.A.class = class A7 { //│ constructor(x) { //│ this.x = x; //│ } //│ get newA() { -//│ return M21.A(); +//│ return M18.A(); //│ } //│ get newB() { //│ return B$(this); @@ -308,7 +259,7 @@ M.A(2).newB.newA_B(3).newB.get //│ toString() { return "A(" + globalThis.Predef.render(this.x) + ")"; } //│ }; //│ scrut = 0; -//│ if (scrut instanceof M21.A.class) { +//│ if (scrut instanceof M18.A.class) { //│ true //│ } else { //│ false @@ -316,9 +267,9 @@ M.A(2).newB.newA_B(3).newB.get //│ } //│ static toString() { return "M"; } //│ }; -//│ tmp6 = M22.A(2); -//│ tmp7 = runtime.safeCall(tmp6.newB.newA_B(3)); -//│ tmp7.newB.get +//│ tmp5 = M19.A(2); +//│ tmp6 = runtime.safeCall(tmp5.newB.newA_B(3)); +//│ tmp6.newB.get //│ = 3 module M with @@ -335,13 +286,3 @@ fun foo(x) = foo(123) //│ = [true, [true, false], true] - -:fixme -fun f = - module M with - fun g = - fun h = M.g - h - M.g -//│ ═══[WARNING] Modules are not yet lifted. -//│ /!!!\ Uncaught error: hkmc2.InternalError: Not in scope: member:M (class hkmc2.semantics.BlockMemberSymbol) From 8ad37dae366b2014ba30f57b6f36e2a237cc72aa Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Tue, 25 Feb 2025 23:41:43 +0800 Subject: [PATCH 127/127] Add missing comma --- hkmc2/shared/src/main/scala/hkmc2/Config.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index 22b5f59e3..819c7bbc0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -25,7 +25,7 @@ object Config: sanityChecks = N, // TODO make the default S // sanityChecks = S(SanityChecks(light = true)), effectHandlers = N, - liftDefns = N + liftDefns = N, ) case class SanityChecks(light: Bool)