diff --git a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala index 3019421f8e..92f25617f7 100644 --- a/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala +++ b/compiler/shared/main/scala/mlscript/compiler/optimizer/TailRecOpt.scala @@ -13,6 +13,8 @@ import compiler.ir.Node._ DOCUMENTATION OF SEMANTICS OF @tailcall and @tailrec +FIXME: This doc is a bit outdated, as we have not ported the "modulo-cons" optimization yet. + @tailcall: Used to annotate specific function calls. Calls annotated with @tailcall must be tail calls or tail modulo-cons calls. These calls must be optimized to not consume additional stack space. If such an optimization is not possible, then the diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index e7b3ae69b8..17a5154e1b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -28,6 +28,16 @@ case class Config( def stackSafety: Opt[StackSafety] = effectHandlers.flatMap(_.stackSafety) + // NOTE: We force the rewriting of while loops to functions when handler lowering is on + // to prevent the "floating out" of definitions done by handler lowering, + // which currently does not respect scopes introduced by `Scoped` blocks. + // Currently, this is only a problem with while loops because we do not yet + // construct nested Scoped blocks in other places (but will in the future). + // see https://github.com/hkust-taco/mlscript/pull/356#discussion_r2579529893 + // and https://github.com/hkust-taco/mlscript/pull/356#discussion_r2585183902 + def shouldRewriteWhile: Bool = + rewriteWhileLoops || effectHandlers.isDefined + end Config @@ -39,7 +49,7 @@ object Config: effectHandlers = N, liftDefns = N, target = CompilationTarget.JS, - rewriteWhileLoops = true, + rewriteWhileLoops = false, stageCode = false, tailRecOpt = true, ) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index d3aa3a5908..cf9e5b77a2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -29,6 +29,35 @@ sealed abstract class Block extends Product: case _: End => true case _ => false + /** This variation of `definedVars` excludes the `syms` in `Scoped` blocks. + * It is used in JSBuilder now: names are allocated in JSBuilder for these `definedVarsNoScoped` symbols. + * This is needed now because + * - there are symbols that are not collected in the `Scoped` blocks (due to some passes that have not yet been adapted to Scoped), + * and they still need to be allocated a name in JSBuilder + * - if we don't exlude the `Scoped` symbols, they may be allocated a name + * prematurely and decalred in wrong places, e.g. symbols inside while bodies + * may be declared in an outer level + */ + lazy val definedVarsNoScoped: Set[Local] = this match + case _: Return | _: Throw => Set.empty + case Begin(sub, rst) => sub.definedVarsNoScoped ++ rst.definedVarsNoScoped + case Assign(l: TermSymbol, r, rst) => rst.definedVarsNoScoped + case Assign(l, r, rst) => rst.definedVarsNoScoped + l + case AssignField(l, n, r, rst) => rst.definedVarsNoScoped + case AssignDynField(l, n, ai, r, rst) => rst.definedVarsNoScoped + case Match(scrut, arms, dflt, rst) => + arms.flatMap(_._2.definedVarsNoScoped).toSet ++ dflt.toList.flatMap(_.definedVarsNoScoped) ++ rst.definedVarsNoScoped + case End(_) => Set.empty + case Break(_) => Set.empty + case Continue(_) => Set.empty + case Define(defn, rst) => + val rest = rst.definedVarsNoScoped + if defn.isOwned then rest else rest + defn.sym + // Note that the handler's LHS and body are not part of the current block, so we do not consider them here. + case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => rst.definedVarsNoScoped + res + case TryBlock(sub, fin, rst) => sub.definedVarsNoScoped ++ fin.definedVarsNoScoped ++ rst.definedVarsNoScoped + case Label(lbl, _, bod, rst) => bod.definedVarsNoScoped ++ rst.definedVarsNoScoped + case Scoped(syms, body) => body.definedVarsNoScoped -- syms lazy val isAbortive: Bool = this match case _: End => false case _: Throw | _: Break | _: Continue => true @@ -42,7 +71,10 @@ sealed abstract class Block extends Product: case TryBlock(sub, fin, rst) => rst.isAbortive || sub.isAbortive || fin.isAbortive case Label(_, _, bod, rst) => rst.isAbortive case HandleBlock(_, _, _, _, _, handlers, body, rst) => rst.isAbortive + case Scoped(_, body) => body.isAbortive + // * Note: there is a good chance that historical users of `definedVars` do not properly respect Scoped blocks + // * and should adapt their logic to use `definedVarsNoScoped` instead. lazy val definedVars: Set[Local] = this match case _: Return | _: Throw => Set.empty case Begin(sub, rst) => sub.definedVars ++ rst.definedVars @@ -62,6 +94,7 @@ sealed abstract class Block extends Product: case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => rst.definedVars + res case TryBlock(sub, fin, rst) => sub.definedVars ++ fin.definedVars ++ rst.definedVars case Label(lbl, _, bod, rst) => bod.definedVars ++ rst.definedVars + case Scoped(syms, body) => body.definedVars lazy val size: Int = this match case _: Return | _: Throw | _: End | _: Break | _: Continue => 1 @@ -75,9 +108,11 @@ sealed abstract class Block extends Product: 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 Scoped(_, body) => body.size // TODO conserve if no changes def mapTail(f: BlockTail => Block): Block = this match + case Scoped(syms, body) => Scoped(syms, body.mapTail(f)) case b: BlockTail => f(b) case Begin(sub, rst) => Begin(sub, rst.mapTail(f)) case Assign(lhs, rhs, rst) => Assign(lhs, rhs, rst.mapTail(f)) @@ -114,6 +149,7 @@ sealed abstract class Block extends Product: 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) + case Scoped(syms, body) => body.freeVars case End(msg) => Set.empty lazy val freeVarsLLIR: Set[Local] = this match @@ -134,6 +170,7 @@ sealed abstract class Block extends Product: case Define(defn, rest) => defn.freeVarsLLIR ++ (rest.freeVarsLLIR - defn.sym) case HandleBlock(lhs, res, par, args, cls, hdr, bod, rst) => (bod.freeVarsLLIR - lhs) ++ rst.freeVarsLLIR ++ hdr.flatMap(_.freeVarsLLIR) + case Scoped(syms, body) => body.freeVarsLLIR case End(msg) => Set.empty lazy val subBlocks: Ls[Block] = this match @@ -146,6 +183,7 @@ sealed abstract class Block extends Product: case Define(d, rest) => d.subBlocks ::: rest :: Nil case HandleBlock(_, _, par, args, _, handlers, body, rest) => par.subBlocks ++ args.flatMap(_.subBlocks) ++ handlers.map(_.body) :+ body :+ rest case Label(_, _, body, rest) => body :: rest :: Nil + case Scoped(_, body) => body :: Nil // TODO rm Lam from values and thus the need for these cases case Return(r, _) => r.subBlocks @@ -160,10 +198,10 @@ sealed abstract class Block extends Product: // 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: Defn => Bool = _ => false, - preserve: Defn => Bool = _ => false - ) = + def extractDefns( + ignore: Defn => Bool = _ => false, + preserve: Defn => Bool = _ => false + ): (Block, List[Defn]) = var defns: List[Defn] = Nil val transformer = new BlockTransformerShallow(SymbolSubst()): override def applyBlock(b: Block): Block = b match @@ -177,6 +215,12 @@ sealed abstract class Block extends Product: (transformer.applyBlock(this), defns) + def gatherDefns( + ignore: Defn => Bool = _ => false, + preserve: Defn => Bool = _ => false + ): List[Defn] = extractDefns(ignore, preserve)._2 // TODO: fix this very inefficient implementation + + lazy val flattened: Block = this.flatten(identity) private def flatten(k: End => Block): Block = this match @@ -196,6 +240,13 @@ sealed abstract class Block extends Product: if (newBody is body) && (newRest is rest) then this else Label(label, loop, newBody, newRest) + + // * Do not omit the `Begin`s that are used for nested scopes + case Begin(e: End, Scoped(syms, body)) => + val newBody = body.flatten(k) + if newBody is body + then this + else new Begin(e, new Scoped(syms, newBody)) case Begin(sub, rest) => sub.flatten(_ => rest.flatten(k)) @@ -260,6 +311,12 @@ sealed abstract class Block extends Product: then this else HandleBlock(lhs, res, par, args, cls, newHandlers, newBody, newRest) + case Scoped(syms, body) => + val newBody = body.flatten(k) + if newBody is body + then this + else Scoped(syms, newBody) + case e: End => k(e) case t: BlockTail => this @@ -285,6 +342,9 @@ case class Label(label: Local, loop: Bool, body: Block, rest: Block) extends Blo case class Break(label: Local) extends BlockTail case class Continue(label: Local) extends BlockTail + +case class Scoped(syms: collection.Set[Local], body: Block) extends BlockTail + // TODO: remove this form? case class Begin(sub: Block, rest: Block) extends Block with ProductWithTail @@ -299,6 +359,36 @@ case class AssignDynField(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, res case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail +object Label: + def apply(label: Local, loop: Bool, body: Block, rest: Block): Block = rest match + case Scoped(syms, rest) => Scoped(syms, Label(label, loop, body, rest)) + case _ => new Label(label, loop, body, rest) +object Scoped: + def apply(syms: collection.Set[Local], body: Block): Block = body match + case Scoped(syms2, body) => + if syms2.isEmpty && syms.isEmpty then Scoped(Set.empty, body) else Scoped(syms ++ syms2, body) + case _ => + if syms.isEmpty then body else new Scoped(syms, body) +object TryBlock: + def apply(sub: Block, finallyDo: Block, rest: Block): Block = rest match + case Scoped(syms, body) => Scoped(syms, TryBlock(sub, finallyDo, body)) + case _ => new TryBlock(sub, finallyDo, rest) +object Assign: + def apply(lhs: Local, rhs: Result, rest: Block): Block = rest match + case Scoped(syms, body) => Scoped(syms, Assign(lhs, rhs, body)) + case _ => new Assign(lhs, rhs, rest) +object AssignField: + def apply(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block)(symbol: Opt[MemberSymbol]): Block = rest match + case Scoped(syms, body) => Scoped(syms, AssignField(lhs, nme, rhs, body)(symbol)) + case _ => new AssignField(lhs, nme, rhs, rest)(symbol) +object AssignDynField: + def apply(lhs: Path, fld: Path, arrayIdx: Bool, rhs: Result, rest: Block): Block = rest match + case Scoped(syms, body) => Scoped(syms, AssignDynField(lhs, fld, arrayIdx, rhs, body)) + case _ => new AssignDynField(lhs, fld, arrayIdx, rhs, rest) +object Define: + def apply(defn: Defn, rest: Block): Block = rest match + case Scoped(syms, body) => Scoped(syms, Define(defn, body)) + case _ => new Define(defn, rest) object Match: def apply(scrut: Path, arms: Ls[Case -> Block], dflt: Opt[Block], rest: Block): Block = dflt match @@ -308,13 +398,20 @@ object Match: case _ => if !rest.isEmpty && arms.forall(_._2.isAbortive) && dflt.exists(_.isAbortive) then new Match(scrut, arms, dflt, End("unreachable")) - else new Match(scrut, arms, dflt, rest) + else rest match + case Scoped(syms, body) => Scoped(syms, Match(scrut, arms, dflt, body)) + case _ => new Match(scrut, arms, dflt, rest) object Begin: def apply(sub: Block, rest: Block): Block = if sub.isEmpty then rest else if sub.isAbortive then sub - else new Begin(sub, rest) + else (sub, rest) match + case (Scoped(symsSub, bodySub), Scoped(symsRest, bodyRest)) => + Scoped(symsSub ++ symsRest, Begin(bodySub, bodyRest)) + case (Scoped(symsSub, bodySub), _) => Scoped(symsSub, Begin(bodySub, rest)) + case (_, Scoped(symsRest, bodyRest)) => Scoped(symsRest, Begin(sub, bodyRest)) + case _ => new Begin(sub, rest) case class HandleBlock( @@ -328,6 +425,40 @@ case class HandleBlock( rest: Block ) extends Block with ProductWithTail +object HandleBlock: + def apply( + lhs: Local, + res: Local, + par: Path, + args: Ls[Path], + cls: ClassSymbol, + handlers: Ls[Handler], + body: Block, + rest: Block + ) = rest match + case Scoped(syms, rest) => + Scoped( + syms, + new HandleBlock( + lhs, + res, + par, + args, + cls, + handlers, + body, + rest + )) + case _ => new HandleBlock( + lhs, + res, + par, + args, + cls, + handlers, + body, + rest) + sealed abstract class Defn: val innerSym: Opt[MemberSymbol] diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index b57747173a..a255dd0742 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -95,6 +95,9 @@ class BlockTransformer(subst: SymbolSubst): if (lhs2 is lhs) && (fld2 is fld) && (rhs2 is rhs) && (rest2 is rest) then b else AssignDynField(lhs2, fld2, arrayIdx, rhs2, rest2) + case Scoped(s, bd) => + val nb = applySubBlock(bd) + if nb is bd then b else Scoped(s, nb) def applyRcdArg(rcdArg: RcdArg)(k: RcdArg => Block): Block = val RcdArg(idx, p) = rcdArg diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala index 5d1137e5e5..4d95df34cd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTraverser.scala @@ -51,6 +51,7 @@ class BlockTraverser: applyResult(rhs) applyPath(fld) applySubBlock(rest) + case Scoped(_, body) => applySubBlock(body) def applyResult(r: Result): Unit = r match case r @ Call(fun, args) => applyPath(fun); args.foreach(applyArg) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index b35e350613..23707ade81 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -420,6 +420,7 @@ class HandlerLowering(paths: HandlerPaths, opt: EffectHandlers)(using TL, Raise, // ignored cases case TryBlock(sub, finallyDo, rest) => ??? // ignore case Throw(_) => PartRet(blk, Nil) + case Scoped(_, body) => go(body) case _: HandleBlock => lastWords("unexpected handleBlock") // already translated at this point val PartRet(head, states) = go(blk)(using labelIds, N) @@ -581,7 +582,7 @@ class HandlerLowering(paths: HandlerPaths, opt: EffectHandlers)(using TL, Raise, 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.extractDefns() defns.foldLeft(blk)((acc, defn) => Define(defn, acc)) private def locToStr(l: Loc): Str = diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 6d237681b7..99f5caec51 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -546,8 +546,10 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): extension (b: Block) private def floatOut(ctx: LifterCtx) = - b.floatOutDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) - + b.extractDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) + private def gather(ctx: LifterCtx) = + b.gatherDefns(preserve = defn => ctx.isModOrObj(defn.sym) || ctx.ignored(defn.sym)) + def createLiftInfoCont(d: Defn, parentCls: Opt[ClsLikeDefn], ctx: LifterCtx): Map[BlockMemberSymbol, LiftedInfo] = val AccessInfo(accessed, _, refdDefns) = ctx.getAccesses(d.sym) @@ -600,7 +602,7 @@ class Lifter(handlerPaths: Opt[HandlerPaths])(using State, Raise): 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 ++ c.companion.fold(Nil)(_.ctor.floatOut(ctx)._2) + val defns = c.preCtor.gather(ctx) ++ c.ctor.gather(ctx) ++ c.companion.fold(Nil)(_.ctor.gather(ctx)) val newCtx = if (c.companion.isDefined) && !ctx.ignored(c.sym) then ctx else ctx.addClsDefn(c) val staticMtdInfo = c.companion.fold(Map.empty): case value => value.methods.flatMap(f => createLiftInfoFn(f, newCtx)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index ca6242e94c..e7a01327f4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -36,8 +36,16 @@ object Thrw extends TailOp: // * No longer in meaningful use and could be removed if we don't find a use for it: -class LoweringCtx(initMap: Map[Local, Value], val mayRet: Bool): +class LoweringCtx( + initMap: Map[Local, Value], + val mayRet: Bool, + private val definedSymsDuringLowering: collection.mutable.Set[Symbol] +): val map = initMap + + def collectScopedSym(s: Symbol) = definedSymsDuringLowering.add(s) + def collectScopedSyms(s: Symbol*) = definedSymsDuringLowering.addAll(s) + def getCollectedSym: collection.Set[Symbol] = definedSymsDuringLowering /* def +(kv: (Local, Value)): Subst = kv match @@ -50,12 +58,13 @@ class LoweringCtx(initMap: Map[Local, Value], val mayRet: Bool): case Value.Ref(l, _) => map.getOrElse(l, v) case _ => v object LoweringCtx: - val empty = LoweringCtx(Map.empty, false) - def subst(using sub: LoweringCtx): LoweringCtx = sub - def nestFunc(using sub: LoweringCtx): LoweringCtx = LoweringCtx(sub.map, true) + val empty = LoweringCtx(Map.empty, false, collection.mutable.Set.empty) + def loweringCtx(using sub: LoweringCtx): LoweringCtx = sub + def nestFunc(using sub: LoweringCtx): LoweringCtx = LoweringCtx(sub.map, true, sub.definedSymsDuringLowering) + def nestScoped(using sub: LoweringCtx): LoweringCtx = LoweringCtx(sub.map, sub.mayRet, collection.mutable.Set.empty) end LoweringCtx -import LoweringCtx.subst +import LoweringCtx.loweringCtx class Lowering()(using Config, TL, Raise, State, Ctx): @@ -189,12 +198,10 @@ class Lowering()(using Config, TL, Raise, State, Ctx): subTerm(lhs): l => subTerm_nonTail(rhs): r => blockImpl(stats, L((mut, RcdArg(S(l), r) :: flds)))(k) - case (decl @ LetDecl(sym, annotations)) :: (stats @ ((_: DefineVar) :: _)) => - reportAnnotations(decl, annotations) - blockImpl(stats, res)(k) case (decl @ LetDecl(sym, annotations)) :: stats => reportAnnotations(decl, annotations) - blockImpl(DefineVar(sym, Term.Lit(Tree.UnitLit(false))) :: stats, res)(k) + if sym.asTrm.forall(_.owner.isEmpty) then loweringCtx.collectScopedSym(sym) + blockImpl(stats, res)(k) case DefineVar(sym, rhs) :: stats => term(rhs): r => Assign(sym, r, blockImpl(stats, res)(k)) @@ -208,6 +215,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx): d match case td: TermDefinition => reportAnnotations(td, td.extraAnnotations) + if td.owner.isEmpty && td.hasDeclareModifier.isEmpty then + loweringCtx.collectScopedSym(td.sym) td.body match case N => // abstract declarations have no lowering blockImpl(stats, res)(k) @@ -245,6 +254,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): reportAnnotations(cls, cls.extraAnnotations) blockImpl(stats, res)(k) case _defn: ClassLikeDef => + if _defn.owner.isEmpty then loweringCtx.collectScopedSym(_defn.bsym) val defn = _defn match case cls: ClassDef => cls case mod: ModuleOrObjectDef if mod.kind is syntax.Mod => // * Currently, both objects and modules are represented as `ModuleOrObjectDef`s @@ -293,7 +303,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val mtds = methods.map: case (sym, params, split) => val paramLists = params :: Nil - val bodyBlock = ucs.Normalization(this)(split)(Ret) + val bodyBlock = inScopedBlock(ucs.Normalization(this)(split)(Ret)) FunDefn.withFreshSymbol(N, sym, paramLists, bodyBlock)(forceTailRec = false) // The return type is intended to be consistent with `gatherMembers` (mtds, Nil, Nil, End()) @@ -331,7 +341,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): case S(ext) => assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls subTerm(ext.cls): clsp => - val pctor = parentConstructor(ext.cls, ext.args) + val pctor = inScopedBlock(parentConstructor(ext.cls, ext.args)) Define( ClsLikeDefn( defn.owner, defn.sym, defn.bsym, defn.kind, defn.paramsOpt, defn.auxParams, S(clsp), @@ -435,12 +445,13 @@ class Lowering()(using Config, TL, Raise, State, Ctx): // * (non-local functions are compiled into getter methods selected on some prefix) if td.params.isEmpty then val l = new TempSymbol(S(ref)) + loweringCtx.collectScopedSym(l) return Assign(l, Call(Value.Ref(bs, disamb).withLocOf(ref), Nil)(true, true, annots.contains(Annot.TailCall)), k(Value.Ref(l, disamb))) case S(_) => () case N => () // TODO panic here; can only lower refs to elab'd symbols case _ => () warnStmt - k(subst(Value.Ref(sym, disamb).withLocOf(ref))) + k(loweringCtx(Value.Ref(sym, disamb).withLocOf(ref))) @tailrec final def term(t: st, inStmtPos: Bool = false)(k: Result => Block)(using LoweringCtx): Block = @@ -510,7 +521,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val isOr = sym is State.orSymbol if isAnd || isOr then val lamSym = BlockMemberSymbol("lambda", Nil, false) - val lamDef = FunDefn.withFreshSymbol(N, lamSym, PlainParamList(Nil) :: Nil, returnedTerm(arg2))(forceTailRec = false) + loweringCtx.collectScopedSym(lamSym) + val lamDef = FunDefn.withFreshSymbol( + N, + lamSym, + PlainParamList(Nil) :: Nil, + inScopedBlock(returnedTerm(arg2)))(forceTailRec = false) Define( lamDef, k(Call( @@ -578,6 +594,20 @@ class Lowering()(using Config, TL, Raise, State, Ctx): t.toLoc :: Nil, source = Diagnostic.Source.Compilation) conclude(Value.Ref(ctx.builtins.debug.getLocals, N).withLocOf(f)) + case t if instantiatedResolvedBms.exists(_ is ctx.builtins.scope.locally) => + arg match + case Tup(Fld(FldFlags.benign(), body, N) :: Nil) => + LoweringCtx.nestScoped.givenIn: + val res = block(Nil, R(body))(k) + val scopedSyms = loweringCtx.getCollectedSym + // Put the Scoped in the rest, so that the returned result can be found correctly + new Begin(End(), new Scoped(scopedSyms, res)) + case _ => + return fail: + ErrorReport( + msg"Unsupported form for scope.locally." -> + t.toLoc :: Nil, + source = Diagnostic.Source.Compilation) // * Due to whacky JS semantics, we need to make sure that selections leading to a call // * are preserved in the call and not moved to a temporary variable. case sel @ Sel(prefix, nme) => @@ -611,6 +641,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): S(Handler(td.sym, resumeSym, paramLists, bodyBlock)) }.collect{ case Some(v) => v } val resSym = TempSymbol(S(t)) + loweringCtx.collectScopedSym(resSym) subTerm(rhs): par => subTerms(as): asr => HandleBlock(lhs, resSym, par, asr, cls, handlers, @@ -647,6 +678,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): then k(Lambda(paramLists.head, bodyBlock)) else val lamSym = new BlockMemberSymbol("lambda", Nil, false) + loweringCtx.collectScopedSym(lamSym) val lamDef = FunDefn.withFreshSymbol(N, lamSym, paramLists, bodyBlock)(forceTailRec = false) Define( lamDef, @@ -697,11 +729,14 @@ class Lowering()(using Config, TL, Raise, State, Ctx): inner => lowerArg(arg): asr2 => val ts = TempSymbol(N) + loweringCtx.collectScopedSym(ts) Assign(ts, Call(inner, asr2)(true, true, false), acc(Value.Ref(ts))) val ts = TempSymbol(N) + loweringCtx.collectScopedSym(ts) Assign(ts, Instantiate(mut, sr, asr), z(Value.Ref(ts))) case S((isym, rft)) => val sym = new BlockMemberSymbol(isym.name, Nil) + loweringCtx.collectScopedSym(sym) val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(rft) val pctor = parentConstructor(cls, as) val clsDef = ClsLikeDefn(N, isym, sym, syntax.Cls, N, Nil, S(sr), @@ -711,6 +746,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): case Try(sub, finallyDo) => val l = new TempSymbol(S(sub)) + loweringCtx.collectScopedSym(l) TryBlock( subTerm_nonTail(sub)(p => Assign(l, p, End())), subTerm_nonTail(finallyDo)(_ => End()), @@ -917,11 +953,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx): reportAnnotations(decl, annotations) sym val ctor = - term_nonTail(Blk(clsBody.nonMethods, clsBody.blk.res))(ImplctRet) - // * This is just a minor improvement to get `constructor() {}` instead of `constructor() { null }` - .mapTail: - case Return(Value.Lit(syntax.Tree.UnitLit(true)), true) => End() - case t => t + inScopedBlock: + term_nonTail(Blk(clsBody.nonMethods, clsBody.blk.res))(ImplctRet) + // * This is just a minor improvement to get `constructor() {}` instead of `constructor() { null }` + .mapTail: + case Return(Value.Lit(syntax.Tree.UnitLit(true)), true) => End() + case t => t (mtds, publicFlds, privateFlds, ctor) def args(elems: Ls[Elem])(k: Ls[Arg] => Block)(using LoweringCtx): Block = @@ -958,6 +995,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): Begin(b, k(asr.reverse)) else val rcdSym = new TempSymbol(N, "rcd") + loweringCtx.collectScopedSym(rcdSym) Begin( b, Assign( @@ -987,10 +1025,12 @@ class Lowering()(using Config, TL, Raise, State, Ctx): case p: Path => k(p) case Lambda(params, body) => val lamSym = BlockMemberSymbol("lambda", Nil, false) + loweringCtx.collectScopedSym(lamSym) val lamDef = FunDefn.withFreshSymbol(N, lamSym, params :: Nil, body)(forceTailRec = false) Define(lamDef, k(lamDef.asPath)) case r => val l = new TempSymbol(N) + loweringCtx.collectScopedSym(l) Assign(l, r, k(l |> Value.Ref.apply)) @@ -998,7 +1038,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx): val (imps, funs, rest) = splitBlock(main.stats, Nil, Nil, Nil) - val blk = block(funs ::: rest, R(main.res))(ImplctRet)(using LoweringCtx.empty) + val blk = + inScopedBlock(using LoweringCtx.empty): + block(funs ::: rest, R(main.res))(ImplctRet) val desug = LambdaRewriter.desugar(blk) @@ -1046,9 +1088,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx): case ps => ps setupFunctionDef(physicalParams, bodyTerm, name) + def inScopedBlock(using LoweringCtx)(mkBlock: LoweringCtx ?=> Block): Block = + LoweringCtx.nestScoped.givenIn: + val body = mkBlock + val scopedSyms = loweringCtx.getCollectedSym + Scoped(scopedSyms, body) + def setupFunctionDef(paramLists: List[ParamList], bodyTerm: Term, name: Option[Str]) (using LoweringCtx): (List[ParamList], Block) = - (paramLists, returnedTerm(bodyTerm)) + val scopedBody = inScopedBlock(returnedTerm(bodyTerm)) + (paramLists, scopedBody) def reportAnnotations(target: Statement, annotations: Ls[Annot]): Unit = def warn(annot: Annot, msg: Opt[Message] = N) = raise: @@ -1096,6 +1145,7 @@ trait LoweringSelSanityChecks(using Config, TL, Raise, State) if !instrument then return super.setupSelection(prefix, nme, disamb)(k) subTerm(prefix): p => val selRes = TempSymbol(N, "selRes") + loweringCtx.collectScopedSym(selRes) // * We are careful to access `x.f` before `x.f$__checkNotMethod` in case `x` is, eg, `undefined` and // * the access should throw an error like `TypeError: Cannot read property 'f' of undefined`. val b0 = blockBuilder @@ -1103,14 +1153,17 @@ trait LoweringSelSanityChecks(using Config, TL, Raise, State) (if disamb.isDefined then // * If the symbol is known, the resolver will have already checked the access [invariant:1] b0 - else b0 - .assign(TempSymbol(N, "discarded"), Select(p, Tree.Ident(nme.name+"$__checkNotMethod"))(N))) - .ifthen(selRes.asPath, - Case.Lit(syntax.Tree.UnitLit(false)), - Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N), - Value.Lit(syntax.Tree.StrLit(s"Access to required field '${nme.name}' yielded 'undefined'")).asArg :: Nil)) - ) - .rest(k(selRes.asPath)) + else + val discardedSym = TempSymbol(N, "discarded") + loweringCtx.collectScopedSym(discardedSym) + b0 + .assign(discardedSym, Select(p, Tree.Ident(nme.name+"$__checkNotMethod"))(N))) + .ifthen(selRes.asPath, + Case.Lit(syntax.Tree.UnitLit(false)), + Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N), + Value.Lit(syntax.Tree.StrLit(s"Access to required field '${nme.name}' yielded 'undefined'")).asArg :: Nil)) + ) + .rest(k(selRes.asPath)) @@ -1155,7 +1208,7 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) case h :: t => go(t, Term.Lam(h, bod)) go(paramLists.reverse, bod) - def setupFunctionBody(params: ParamList, bod: Term, name: Option[Str])(using LoweringCtx): Block = + def setupFunctionBody(params: ParamList, bod: Term, name: Option[Str])(using LoweringCtx): Block = inScopedBlock: val enterMsgSym = TempSymbol(N, dbgNme = "traceLogEnterMsg") val prevIndentLvlSym = TempSymbol(N, dbgNme = "traceLogPrevIndent") val resSym = TempSymbol(N, dbgNme = "traceLogRes") @@ -1163,11 +1216,22 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) val psInspectedSyms = params.params.map(p => TempSymbol(N, dbgNme = s"traceLogParam_${p.sym.nme}") -> p.sym) val resInspectedSym = TempSymbol(N, dbgNme = "traceLogResInspected") + loweringCtx.collectScopedSyms( + enterMsgSym, + prevIndentLvlSym, + resSym, + retMsgSym, + resInspectedSym) + for (s, _) <- psInspectedSyms do loweringCtx.collectScopedSym(s) + val psSymArgs = psInspectedSyms.zipWithIndex.foldRight[Ls[Arg]](Arg(N, Value.Lit(Tree.StrLit(")"))) :: Nil): case (((s, p), i), acc) => if i == psInspectedSyms.length - 1 then Arg(N, Value.Ref(s)) :: acc else Arg(N, Value.Ref(s)) :: Arg(N, Value.Lit(Tree.StrLit(", "))) :: acc + val tmp1, tmp2, tmp3 = TempSymbol(N) + loweringCtx.collectScopedSyms(tmp1, tmp2, tmp3) + assignStmts(psInspectedSyms.map: (pInspectedSym, pSym) => pInspectedSym -> pureCall(inspectFn, Arg(N, Value.Ref(pSym)) :: Nil) *) |>: @@ -1176,7 +1240,7 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) strConcatFn, Arg(N, Value.Lit(Tree.StrLit(s"CALL ${name.getOrElse("[arrow function]")}("))) :: psSymArgs ), - TempSymbol(N) -> pureCall(traceLogFn, Arg(N, Value.Ref(enterMsgSym)) :: Nil), + tmp1 -> pureCall(traceLogFn, Arg(N, Value.Ref(enterMsgSym)) :: Nil), prevIndentLvlSym -> pureCall(traceLogIndentFn, Nil) ) |>: term(bod)(r => @@ -1187,11 +1251,11 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) strConcatFn, Arg(N, Value.Lit(Tree.StrLit("=> "))) :: Arg(N, Value.Ref(resInspectedSym)) :: Nil ), - TempSymbol(N) -> pureCall(traceLogResetFn, Arg(N, Value.Ref(prevIndentLvlSym)) :: Nil), - TempSymbol(N) -> pureCall(traceLogFn, Arg(N, Value.Ref(retMsgSym)) :: Nil) + tmp2 -> pureCall(traceLogResetFn, Arg(N, Value.Ref(prevIndentLvlSym)) :: Nil), + tmp3 -> pureCall(traceLogFn, Arg(N, Value.Ref(retMsgSym)) :: Nil) ) |>: Ret(Value.Ref(resSym)) - )(using LoweringCtx.nestFunc) + ) object TrivialStatementsAndMatch: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 34a3cfe045..00dc4e6160 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -55,6 +55,7 @@ object Printer: doc"set ${mkDocument(lhs)}.${nme.name} = ${mkDocument(rhs)} in # ${mkDocument(rest)}" case Define(defn, rest) => doc"define ${mkDocument(defn)} in # ${mkDocument(rest)}" + case Scoped(_, body) => mkDocument(body) case End("") => doc"end" case End(msg) => doc"end ${msg}" case _ => TODO(blk) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala index a98afafca9..977d240fc7 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/StackSafeTransform.scala @@ -166,11 +166,11 @@ class StackSafeTransform(depthLimit: Int, paths: HandlerPaths, doUnwindMap: Map[ // However, due to how tightly coupled the stack safety and handler lowering are, it might be // better to simply merge the two passes in the future. val (blk, defns) = doUnwindPath.get match - case Value.Ref(sym, _) => rewritten.floatOutDefns() + case Value.Ref(sym, _) => rewritten.extractDefns() case _ => (rewritten, Nil) defns.foldLeft(blk)((acc, defn) => Define(defn, acc)) - - + + def rewriteFn(defn: FunDefn) = if doUnwindFns.contains(defn.sym) then defn else FunDefn(defn.owner, defn.sym, defn.dSym, defn.params, rewriteBlk(defn.body, L(defn.sym), 1))(defn.forceTailRec) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala index 3bb85d90d3..690da38c08 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala @@ -362,7 +362,7 @@ class TailRecOpt(using State, TL, Raise): c.copy(methods = mtds, companion = companion) def transform(b: Block) = - val (blk, defns) = b.floatOutDefns() + val defns = b.gatherDefns() val (funs, clses) = defns.partitionMap: case f: FunDefn => L(f) case c: ClsLikeDefn => R(c) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala index 622e5d8f0d..026610a470 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/UsedVarAnalyzer.scala @@ -48,9 +48,9 @@ class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): existingVars += (f.sym -> existing) val thisVars = Lifter.getVars(f) -- existing val newExisting = existing ++ thisVars - - val thisScopeDefns: List[Defn] = f.body.floatOutDefns()._2 - + + val thisScopeDefns: List[Defn] = f.body.gatherDefns() + nestedDefns += f.sym -> thisScopeDefns val newInScope = inScope ++ thisScopeDefns.map(_.sym) @@ -83,8 +83,8 @@ class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): val thisVars = Lifter.getVars(c) -- existing val newExisting = existing ++ thisVars - val thisScopeDefns: List[Defn] = c.methods ++ c.preCtor.floatOutDefns()._2 - ++ c.ctor.floatOutDefns()._2 ++ c.companion.fold(Nil)(comp => comp.ctor.floatOutDefns()._2 ++ comp.methods) + val thisScopeDefns: List[Defn] = c.methods ++ c.preCtor.gatherDefns() + ++ c.ctor.gatherDefns() ++ c.companion.fold(Nil)(comp => comp.ctor.gatherDefns() ++ comp.methods) nestedDefns += c.sym -> thisScopeDefns @@ -271,7 +271,7 @@ class UsedVarAnalyzer(b: Block, handlerPaths: Opt[HandlerPaths])(using State): // 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.gatherDefns() val defnSyms = defns.collect: case f: FunDefn => f.sym -> f case c: ClsLikeDefn => c.sym -> c 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 7b9d9c1e32..6ca873a496 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -335,13 +335,14 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: (doc" # ${getVar(sym, sym.toLoc)} = this;", fz) else (doc"", doc"") - val preCtorCode = body(preCtor, true) - val ctorCode = doc"$preCtorCode$singletonInit${body(ctor, endSemi = true)}${ - kind match - case syntax.Obj => - doc" # ${defineProperty(doc"this", "class", doc"${scope.lookup_!(isym, isym.toLoc)}")};" - case _ => "" - }$singletonFreeze" + val ctorCode = scope.nest.givenIn: + val preCtorCode = nonNestedScoped(preCtor)(bd => block(bd, true)) + doc"$preCtorCode$singletonInit${nonNestedScoped(ctor)(bd => block(bd, true))}${ + kind match + case syntax.Obj => + doc" # ${defineProperty(doc"this", "class", doc"${scope.lookup_!(isym, isym.toLoc)}")};" + case _ => "" + }$singletonFreeze" // * If there are no ctor params, pop one param list off the aux params val (newCtorAuxParams, initialCtorParams) = paramsOpt match @@ -476,14 +477,14 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: case Match(scrut, Nil, els, rest) => val e = els match - case S(el) => returningTerm(el, endSemi = true) + case S(el) => nonNestedScoped(el)(bod => returningTerm(bod, endSemi = true)) case N => doc"" e :: returningTerm(rest, endSemi) case Match(scrut, arms, els, rest) if arms.sizeCompare(1) > 0 && arms.forall(_._1.isInstanceOf[Case.Lit]) => val l = arms.foldLeft(doc""): (acc, arm) => acc :: doc" # case ${arm._1.asInstanceOf[Case.Lit].lit.idStr}: #{ ${ - returningTerm(arm._2, endSemi = true) + nonNestedScoped(arm._2)(bd => returningTerm(bd, endSemi = true)) } # break; #} " val e = els match case S(el) => @@ -512,12 +513,12 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: doc"""typeof $sd === "object" && $sd !== null && "${n.name}" in $sd""" case Case.Field(name = n, safe = true) => doc""""${n.name}" in $sd""" - val h = doc" # if (${ cond(hd._1) }) ${ braced(returningTerm(hd._2, endSemi = false)) }" + val h = doc" # if (${ cond(hd._1) }) ${ braced(nonNestedScoped(hd._2)(res => returningTerm(res, endSemi = false))) }" val t = tl.foldLeft(h)((acc, arm) => - acc :: doc" else if (${ cond(arm._1) }) ${ braced(returningTerm(arm._2, endSemi = false)) }") + acc :: doc" else if (${ cond(arm._1) }) ${ braced(nonNestedScoped(arm._2)(res => returningTerm(res, endSemi = false))) }") val e = els match case S(el) => - doc" else ${ braced(returningTerm(el, endSemi = false)) }" + doc" else ${ braced(nonNestedScoped(el)(res => returningTerm(res, endSemi = false))) }" case N => doc"" t :: e :: returningTerm(rest, endSemi) @@ -543,7 +544,7 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: // [fixme:0] TODO check scope and allocate local variables here (see: https://github.com/hkust-taco/mlscript/pull/293#issuecomment-2792229849) doc" # ${getVar(lbl, lbl.toLoc)}:${if loop then doc" while (true)" else ""} " :: braced { - returningTerm(bod, endSemi = true) :: (if loop then doc" # break;" else doc"") + nonNestedScoped(bod)(bd => returningTerm(bd, endSemi = true)) :: (if loop then doc" # break;" else doc"") } :: returningTerm(rst, endSemi) case TryBlock(sub, fin, rst) => @@ -551,6 +552,24 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: braced(returningTerm(fin, endSemi = false)) } # ${ returningTerm(rst, endSemi).stripBreaks}" + + // Only nested scopes will be handled here. + case Scoped(syms, body) => + scope.nest.givenIn: + val vars = syms.toArray.sortBy(_.uid).iterator.flatMap: l => + if scope.lookup(l).isDefined then // FIXME: this logic is incorrect and should eventually be removed + // NOTE: this warning is turned off because the lifter is not + // yet updated to maintian the Scoped blocks, so when + // something is lifted out, its symbol may be already declared in an outer level, + // but the inner Scoped block still contains the same symbol + // raise: + // WarningReport(msg"var ${l.toString()} in scoped is already allocated" -> N :: Nil) + // Some(l -> s"${scope.lookup_!(l, N)}_again") + None + else + Some(l -> scope.allocateName(l)) + braced: + genLetDecls(vars) :: returningTerm(body, endSemi) // case _ => ??? @@ -615,34 +634,53 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder: then "./" + os.Path(path).relativeTo(wd).toString else path doc"""import ${getVar(i._1, N)} from "${relPath}";""" - imps.mkDocument(doc" # ") :/: block(p.main, endSemi = false).stripBreaks :: ( + imps.mkDocument(doc" # ") + :/: nonNestedScoped(p.main)(block(_, endSemi = false)).stripBreaks + :: locally: exprt match - case S(sym) => doc"\nlet ${sym.nme} = ${scope.lookup_!(sym, sym.toLoc)}; export default ${sym.nme};\n" - case N => doc"" - ) + case S(sym) => doc"\nlet ${sym.nme} = ${scope.lookup_!(sym, sym.toLoc)}; export default ${sym.nme};\n" + case N => doc"" def worksheet(p: Program)(using Raise, Scope): (Document, Document) = reserveNames(p) lazy val imps = p.imports.map: i => doc"""${getVar(i._1, N)} = await import("${i._2.toString}").then(m => m.default ?? m);""" - blockPreamble(p.imports.map(_._1).toSeq ++ p.main.definedVars.toSeq) -> - (imps.mkDocument(doc" # ") :/: returningTerm(p.main, endSemi = false).stripBreaks) - - def blockPreamble(ss: Iterable[Symbol])(using Raise, Scope): Document = - // TODO document: mutable var assnts require the lookup - val vars = ss.filter(scope.lookup(_).isEmpty).toArray.sortBy(_.uid).iterator.map(l => - l -> scope.allocateName(l)) + p.main match + case Scoped(syms, body) => + blockPreamble(p.imports.map(_._1).toSeq ++ syms.toSeq) -> + (imps.mkDocument(doc" # ") :/: block(body, endSemi = false).stripBreaks) + case body => // TODO: remove body.definedVarsNoScoped after we can handle lambda lifting-related code correctly + blockPreamble(p.imports.map(_._1).toSeq ++ body.definedVarsNoScoped.toSeq) -> + (imps.mkDocument(doc" # ") :/: returningTerm(body, endSemi = false).stripBreaks) + + def genLetDecls(vars: Iterator[(Symbol, Str)]): Document = if vars.isEmpty then doc"" else doc" # let " :: vars.map: (_, nme) => nme .toList.mkDocument(", ") :: doc";" + def blockPreamble(ss: Iterable[Symbol])(using Raise, Scope): Document = + // TODO document: mutable var assnts require the lookup + // TODO: remove the filter and lookup after we can handle lambda lifting-related code correctly + val vars = ss.filter(scope.lookup(_).isEmpty).toArray.sortBy(_.uid).iterator.map(l => + l -> scope.allocateName(l)) + genLetDecls(vars) + + // Only handle non-nested Scoped nodes: we output the bindings, but do not add another brace + def nonNestedScoped(blk: Block)(k: Block => Document)(using Raise, Scope): Document = blk match + case Scoped(syms, body) => + blockPreamble(syms) :: k(body) + case _ => k(blk) + + def block(t: Block, endSemi: Bool)(using Raise, Scope): Document = - blockPreamble(t.definedVars) :: returningTerm(t, endSemi) + val pre = blockPreamble(t.definedVarsNoScoped) + val rest = returningTerm(t, endSemi) + pre :: rest def body(t: Block, endSemi: Bool)(using Raise, Scope): Document = scope.nest givenIn: - block(t, endSemi) + nonNestedScoped(t)(bd => block(bd, endSemi)) def defineProperty(target: Document, prop: Str, value: Document, enumerable: Bool = false): Document = doc"Object.defineProperty(${target}, ${prop.escaped}, ${ 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 2a96b4f2d9..a7c1621a29 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -523,6 +523,7 @@ final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): bBlock(rest)(k)(ct) case Define(_: ClsLikeDefn, rest) => bBlock(rest)(k)(ct) case End(msg) => k(Expr.Literal(Tree.UnitLit(false))) + case Scoped(_, body) => bBlock(body)(k)(ct) case _: Block => val docBlock = blk.showAsTree bErrStop(msg"Unsupported block: $docBlock") @@ -566,6 +567,7 @@ final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): case AssignField(_, _, _, rest) => applyBlock(rest) case AssignDynField(lhs, fld, arrayIdx, rhs, rest) => applyBlock(rest) case Define(defn, rest) => applyDefn(defn); applyBlock(rest) + case Scoped(_, body) => applyBlock(body) case HandleBlock(lhs, res, par, args, cls, handlers, body, rest) => applyBlock(rest) case End(msg) => diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala index 6463d17b2b..8f6dca0f7f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/wasm/text/WatBuilder.scala @@ -706,6 +706,7 @@ class WatBuilder(using TraceLogger, State) extends CodeBuilder: `return`(S(resWat)) + case Scoped(_, body) => returningTerm(body) case Match(scrut, arms, dflt, rst) => val matchLabelSym = TempSymbol(N, "match") val matchLabel = scope.allocateName(matchLabelSym) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 2980288e9b..79102a2c8e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -201,6 +201,8 @@ object Elaborator: val compile = assumeObject("compile") val buffered = assumeObject("buffered") val bufferable = assumeObject("bufferable") + object scope extends VirtualModule(assumeBuiltinMod("scope")): + val locally = assumeObject("locally") def getBuiltinOp(op: Str): Opt[Str] = if getBuiltin(op).isDefined then builtinBinOps.get(op) else N /** Classes that do not use `instanceof` in pattern matching. */ diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala index 9377f0881b..3d0a6429e6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala @@ -237,6 +237,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e topLevel: Bool )(using labels: Labels)(using LoweringCtx): Block = split match case Split.Let(sym, trm, tl) => + LoweringCtx.loweringCtx.collectScopedSym(sym) term_nonTail(trm): r => Assign(sym, r, lowerSplit(tl, cont, topLevel)) case Split.Cons(Branch(scrut, pat, tail), restSplit) => @@ -249,6 +250,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e pat match case FlatPattern.Lit(lit) => mkMatch(Case.Lit(lit) -> lowerSplit(tail, cont, topLevel = false)) case FlatPattern.ClassLike(ctor, symbol, argsOpt, _refined) => + for args <- argsOpt; (arg, _) <- args do LoweringCtx.loweringCtx.collectScopedSym(arg) /** Make a continuation that creates the match. */ def k(ctorSym: ClassLikeSymbol, clsParams: Ls[TermSymbol])(st: Path): Block = val args = argsOpt.map(_.map(_._1)).getOrElse(Nil) @@ -277,6 +279,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e subTerm_nonTail(ctor)(k(mod, Nil)) case FlatPattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> lowerSplit(tail, cont, topLevel = false)) case FlatPattern.Record(entries) => + for (_, s) <- entries do LoweringCtx.loweringCtx.collectScopedSym(s) val objectSym = ctx.builtins.Object mkMatch( // checking that we have an object Case.Cls(objectSym, Value.Ref(BuiltinSymbol(objectSym.nme, false, false, true, false))), @@ -315,112 +318,129 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e def apply(split: Split)(k: Result => Block)(using Config, LoweringCtx): Block = this(split, `if`, N, k) - private def apply(inputSplit: Split, kw: `if`.type | `while`.type, t: Opt[Term], k: Result => Block)(using Config, LoweringCtx) = - var usesResTmp = false - // The symbol of the temporary variable for the result of the `if`-like term. - // It will be created in one of the following situations. - // 1. The continuation `k` is not a tail operation. - // 2. There are shared consequents in the `if`-like term. - // 3. The term is a `while` and the result is used. - lazy val l = - usesResTmp = true - new TempSymbol(t) - // The symbol for the loop label if the term is a `while`. - lazy val loopLabel = new TempSymbol(t) - lazy val f = new BlockMemberSymbol("while", Nil, false) - lazy val tSym = TermSymbol.fromFunBms(f, N) - val normalized = tl.scoped("ucs:normalize"): - normalize(inputSplit)(using VarSet()) - tl.scoped("ucs:normalized"): - tl.log(s"Normalized:\n${normalized.prettyPrint}") - // Collect consequents that are shared in more than one branch. - given labels: Labels = createLabelsForDuplicatedBranches(normalized) - lazy val rootBreakLabel = new TempSymbol(N, "split_root$") - lazy val breakRoot = (r: Result) => Assign(l, r, Break(rootBreakLabel)) - lazy val assignResult = (r: Result) => Assign(l, r, End()) - val loopCont = if config.rewriteWhileLoops - then Return(Call(Value.Ref(f, S(tSym)), Nil)(true, true, false), false) - else Continue(loopLabel) - val cont = - if kw === `while` then - // If the term is a `while`, the action of `else` branches depends on - // whether the the enclosing split is at the top level or not. - R((topLevel: Bool) => (r: Result) => Assign(l, r, if topLevel then End() else loopCont)) - else if labels.isEmpty then - if k.isInstanceOf[TailOp] then - // If there are no shared consequents and the continuation is a tail - // operation, we can call it directly. - L(k) + private def apply(inputSplit: Split, kw: `if`.type | `while`.type, t: Opt[Term], k: Result => Block)(using cfg: Config, outerCtx: LoweringCtx) = + // if it's `while`, we always make sure that loop bodies are properly scoped nestedly + // see https://github.com/hkust-taco/mlscript/pull/356#discussion_r2588412258 + val useNestedScoped = kw === `while` + (if useNestedScoped then LoweringCtx.nestScoped else outerCtx).givenIn: + var usesResTmp = false + // The symbol of the temporary variable for the result of the `if`-like term. + // It will be created in one of the following situations. + // 1. The continuation `k` is not a tail operation. + // 2. There are shared consequents in the `if`-like term. + // 3. The term is a `while` and the result is used. + lazy val l = + usesResTmp = true + val res = new TempSymbol(t) + outerCtx.collectScopedSym(res) + res + // The symbol for the loop label if the term is a `while`. + lazy val loopLabel = new TempSymbol(t) + lazy val f = + val res = new BlockMemberSymbol("while", Nil, false) + outerCtx.collectScopedSym(res) + res + lazy val tSym = TermSymbol.fromFunBms(f, N) + val normalized = tl.scoped("ucs:normalize"): + normalize(inputSplit)(using VarSet()) + tl.scoped("ucs:normalized"): + tl.log(s"Normalized:\n${normalized.prettyPrint}") + // Collect consequents that are shared in more than one branch. + given labels: Labels = createLabelsForDuplicatedBranches(normalized) + lazy val rootBreakLabel = new TempSymbol(N, "split_root$") + lazy val breakRoot = (r: Result) => Assign(l, r, Break(rootBreakLabel)) + lazy val assignResult = (r: Result) => Assign(l, r, End()) + // NOTE: `shouldRewriteWhile` is not the same as `config.rewriteWhileLoops` + // as shouldRewriteWhile is always true when effect handler lowering is on + lazy val loopCont = if config.shouldRewriteWhile + then Return(Call(Value.Ref(f, S(tSym)), Nil)(true, true, false), false) + else Continue(loopLabel) + val cont = + if kw === `while` then + // If the term is a `while`, the action of `else` branches depends on + // whether the the enclosing split is at the top level or not. + R((topLevel: Bool) => (r: Result) => Assign(l, r, if topLevel then End() else loopCont)) + else if labels.isEmpty then + if k.isInstanceOf[TailOp] then + // If there are no shared consequents and the continuation is a tail + // operation, we can call it directly. + L(k) + else + // Otherwise, if the continuation is not a tail operation, we should + // save the result in a temporary variable and call the continuation + // in the end. + L(assignResult) else - // Otherwise, if the continuation is not a tail operation, we should - // save the result in a temporary variable and call the continuation - // in the end. - L(assignResult) - else - // When there are shared consequents, we are forced to save the result - // in the temporary variable nevertheless. Note that `cont` only gets - // called for non-shared consequents, so we should break to the end of - // the entire split after the assignment. - L(breakRoot) - // The main block contains the lowered split, where each shared consequent - // is replaced with a `Break` to the corresponding label. - val mainBlock = - val innermostBlock = lowerSplit(normalized, cont, topLevel = true) - // Wrap the main block in a labelled block for each shared consequent. The - // `rest` of each `Label` is the lowered consequent plus a `Break` to the - // end of the entire `if` term. Otherwise, it will fall through to the outer - // consequent, which is the wrong semantics. - val innerBlock: Block = labels.consequents match - case Nil => innermostBlock - case all @ (head :: tail) => - def wrap(consequents: Ls[(Term, TempSymbol)]): Block = - consequents.foldRight(innermostBlock): - case ((term, label), innerBlock) => - Label(label, false, innerBlock, term_nonTail(term)(breakRoot)) - // There is no need to generate `break` for the outermost split. - if labels.default.isEmpty then - Label(head._2, false, wrap(tail), term_nonTail(head._1)(assignResult)) - else wrap(all) - labels.default match - case S(label) => Label(label, false, innerBlock, throwMatchErrorBlock) - case N => innerBlock - // If there are shared consequents, we need a wrap the entire block in a - // `Label` so that `Break`s in the shared consequents can jump to the end. - val body = if labels.isEmpty then mainBlock else - Label(rootBreakLabel, false, mainBlock, End()) - // Embed the `body` into `Label` if the term is a `while`. - lazy val rest = if usesResTmp then k(Value.Ref(l)) else k(lowering.unit) - val block = - if kw === `while` then - if config.rewriteWhileLoops then - val loopResult = TempSymbol(N) - val isReturned = TempSymbol(N) - val loopEnd: Path = - Select(Value.Ref(State.runtimeSymbol), Tree.Ident("LoopEnd"))(S(State.loopEndSymbol)) - val blk = blockBuilder - .assign(l, Value.Lit(Tree.UnitLit(false))) - .define(FunDefn(N, f, tSym, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd, false)))(forceTailRec = false)) - .assign(loopResult, Call(Value.Ref(f, S(tSym)), Nil)(true, true, false)) - if summon[LoweringCtx].mayRet then - blk - .assign(isReturned, Call(Value.Ref(State.builtinOpsMap("!==")), - loopResult.asPath.asArg :: loopEnd.asArg :: Nil)(true, false, false)) - .ifthen(Value.Ref(isReturned), Case.Lit(Tree.BoolLit(true)), - Return(Value.Ref(loopResult), false), - S(rest) - ) - .end + // When there are shared consequents, we are forced to save the result + // in the temporary variable nevertheless. Note that `cont` only gets + // called for non-shared consequents, so we should break to the end of + // the entire split after the assignment. + L(breakRoot) + // The main block contains the lowered split, where each shared consequent + // is replaced with a `Break` to the corresponding label. + val mainBlock = + val innermostBlock = lowerSplit(normalized, cont, topLevel = true) + // Wrap the main block in a labelled block for each shared consequent. The + // `rest` of each `Label` is the lowered consequent plus a `Break` to the + // end of the entire `if` term. Otherwise, it will fall through to the outer + // consequent, which is the wrong semantics. + val innerBlock: Block = labels.consequents match + case Nil => innermostBlock + case all @ (head :: tail) => + def wrap(consequents: Ls[(Term, TempSymbol)]): Block = + consequents.foldRight(innermostBlock): + case ((term, label), innerBlock) => + Label(label, false, innerBlock, term_nonTail(term)(breakRoot)) + // There is no need to generate `break` for the outermost split. + if labels.default.isEmpty then + Label(head._2, false, wrap(tail), term_nonTail(head._1)(assignResult)) + else wrap(all) + labels.default match + case S(label) => Label(label, false, innerBlock, throwMatchErrorBlock) + case N => innerBlock + // If there are shared consequents, we need a wrap the entire block in a + // `Label` so that `Break`s in the shared consequents can jump to the end. + val body = + Scoped( + if useNestedScoped then LoweringCtx.loweringCtx.getCollectedSym else Set.empty, + if labels.isEmpty then mainBlock else Label(rootBreakLabel, false, mainBlock, End())) + // Embed the `body` into `Label` if the term is a `while`. + lazy val rest = if usesResTmp then k(Value.Ref(l)) else k(lowering.unit) + val block = + if kw === `while` then + // NOTE: `shouldRewriteWhile` is not the same as `config.rewriteWhileLoops` + // as shouldRewriteWhile is always true when effect handler lowering is on + if config.shouldRewriteWhile then + val loopResult = TempSymbol(N) + val isReturned = TempSymbol(N) + outerCtx.collectScopedSym(loopResult) + outerCtx.collectScopedSym(isReturned) + val loopEnd: Path = + Select(Value.Ref(State.runtimeSymbol), Tree.Ident("LoopEnd"))(S(State.loopEndSymbol)) + val blk = blockBuilder + .assign(l, Value.Lit(Tree.UnitLit(false))) + .define(FunDefn(N, f, tSym, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd, false)))(forceTailRec = false)) + .assign(loopResult, Call(Value.Ref(f, S(tSym)), Nil)(true, true, false)) + if summon[LoweringCtx].mayRet then + blk + .assign(isReturned, Call(Value.Ref(State.builtinOpsMap("!==")), + loopResult.asPath.asArg :: loopEnd.asArg :: Nil)(true, false, false)) + .ifthen(Value.Ref(isReturned), Case.Lit(Tree.BoolLit(true)), + Return(Value.Ref(loopResult), false), + S(rest) + ) + .end + else + blk.rest(rest) else - blk.rest(rest) + Begin(Label(loopLabel, true, body, End()), rest) + else if labels.isEmpty && k.isInstanceOf[TailOp] then + body else - Begin(Label(loopLabel, true, body, End()), rest) - else if labels.isEmpty && k.isInstanceOf[TailOp] then - body - else - Begin(body, rest) - scoped("ucs:lowered"): - log(s"Lowered:\n${block.showAsTree}") - block + Begin(body, rest) + scoped("ucs:lowered"): + log(s"Lowered:\n${block.showAsTree}") + block end Normalization object Normalization: diff --git a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs index 5a9d766890..cee42e81b8 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Predef.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Predef.mjs @@ -261,7 +261,7 @@ let Predef1; } static foldr(f) { return (first, ...rest) => { - let len, scrut, i, init, scrut1, tmp, tmp1, tmp2, tmp3; + let len, scrut, i, init, tmp; len = rest.length; scrut = len === 0; if (scrut === true) { @@ -269,18 +269,19 @@ let Predef1; } else { i = len - 1; init = runtime.safeCall(rest.at(i)); - tmp4: while (true) { + tmp1: while (true) { + let scrut1, tmp2, tmp3, tmp4; scrut1 = i > 0; if (scrut1 === true) { - tmp = i - 1; - i = tmp; - tmp1 = runtime.safeCall(rest.at(i)); - tmp2 = runtime.safeCall(f(tmp1, init)); - init = tmp2; - tmp3 = runtime.Unit; - continue tmp4 + tmp2 = i - 1; + i = tmp2; + tmp3 = runtime.safeCall(rest.at(i)); + tmp4 = runtime.safeCall(f(tmp3, init)); + init = tmp4; + tmp = runtime.Unit; + continue tmp1 } else { - tmp3 = runtime.Unit; + tmp = runtime.Unit; } break; } diff --git a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs index d5fcb5145e..2a27b900cf 100644 --- a/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs +++ b/hkmc2/shared/src/test/mlscript-compile/Runtime.mjs @@ -524,66 +524,69 @@ let Runtime1; return Runtime.mkEffect(Runtime.PrintStackEffect, showLocals) } static topLevelEffect(tr, debug) { - let scrut, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; - tmp6: while (true) { + let tmp, tmp1; + tmp2: while (true) { + let scrut, tmp3, tmp4, tmp5, tmp6; scrut = tr.handler === Runtime.PrintStackEffect; if (scrut === true) { - tmp = Runtime.showStackTrace("Stack Trace:", tr, debug, tr.handlerFun); - tmp1 = runtime.safeCall(globalThis.console.log(tmp)); - tmp2 = Runtime.resume(tr.contTrace); - tmp3 = runtime.safeCall(tmp2(runtime.Unit)); - tr = tmp3; - tmp4 = runtime.Unit; - continue tmp6 + tmp3 = Runtime.showStackTrace("Stack Trace:", tr, debug, tr.handlerFun); + tmp4 = runtime.safeCall(globalThis.console.log(tmp3)); + tmp5 = Runtime.resume(tr.contTrace); + tmp6 = runtime.safeCall(tmp5(runtime.Unit)); + tr = tmp6; + tmp = runtime.Unit; + continue tmp2 } else { - tmp4 = runtime.Unit; + tmp = runtime.Unit; } break; } if (tr instanceof Runtime.EffectSig.class) { - tmp5 = "Error: Unhandled effect " + tr.handler.constructor.name; - throw Runtime.showStackTrace(tmp5, tr, debug, false) + tmp1 = "Error: Unhandled effect " + tr.handler.constructor.name; + throw Runtime.showStackTrace(tmp1, tr, debug, false) } else { return tr } } static showStackTrace(header, tr, debug, showLocals) { - let msg, curHandler, atTail, scrut, cur, scrut1, locals, curLocals, loc, loc1, localsMsg, scrut2, scrut3, tmp, tmp1, lambda, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18; + let msg, curHandler, atTail, tmp, tmp1, tmp2, tmp3; msg = header; curHandler = tr.contTrace; atTail = true; if (debug === true) { - tmp19: while (true) { + tmp4: while (true) { + let scrut, cur, scrut1, tmp5, tmp6, tmp7, tmp8; scrut = curHandler !== null; if (scrut === true) { cur = curHandler.next; - tmp20: while (true) { - scrut1 = cur !== null; - if (scrut1 === true) { + tmp9: while (true) { + let scrut2, locals, curLocals, loc, loc1, localsMsg, scrut3, tmp10, tmp11, lambda, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20; + scrut2 = cur !== null; + if (scrut2 === true) { locals = cur.getLocals; - tmp = locals.length - 1; - curLocals = runtime.safeCall(locals.at(tmp)); + tmp10 = locals.length - 1; + curLocals = runtime.safeCall(locals.at(tmp10)); loc = cur.getLoc; if (loc === null) { - tmp1 = "pc=" + cur.pc; + tmp11 = "pc=" + cur.pc; } else { - tmp1 = loc; + tmp11 = loc; } - loc1 = tmp1; + loc1 = tmp11; split_root$: { split_1$: { if (showLocals === true) { - scrut2 = curLocals.locals.length > 0; - if (scrut2 === true) { + scrut3 = curLocals.locals.length > 0; + if (scrut3 === true) { lambda = (undefined, function (l) { let tmp21, tmp22; tmp21 = l.localName + "="; tmp22 = Rendering.render(l.value); return tmp21 + tmp22 }); - tmp2 = runtime.safeCall(curLocals.locals.map(lambda)); - tmp3 = runtime.safeCall(tmp2.join(", ")); - tmp4 = " with locals: " + tmp3; + tmp12 = runtime.safeCall(curLocals.locals.map(lambda)); + tmp13 = runtime.safeCall(tmp12.join(", ")); + tmp14 = " with locals: " + tmp13; break split_root$ } else { break split_1$ @@ -592,54 +595,54 @@ let Runtime1; break split_1$ } } - tmp4 = ""; + tmp14 = ""; } - localsMsg = tmp4; - tmp5 = "\n\tat " + curLocals.fnName; - tmp6 = tmp5 + " ("; - tmp7 = tmp6 + loc1; - tmp8 = tmp7 + ")"; - tmp9 = msg + tmp8; - msg = tmp9; - tmp10 = msg + localsMsg; - msg = tmp10; + localsMsg = tmp14; + tmp15 = "\n\tat " + curLocals.fnName; + tmp16 = tmp15 + " ("; + tmp17 = tmp16 + loc1; + tmp18 = tmp17 + ")"; + tmp19 = msg + tmp18; + msg = tmp19; + tmp20 = msg + localsMsg; + msg = tmp20; cur = cur.next; atTail = false; - tmp11 = runtime.Unit; - continue tmp20 + tmp5 = runtime.Unit; + continue tmp9 } else { - tmp11 = runtime.Unit; + tmp5 = runtime.Unit; } break; } curHandler = curHandler.nextHandler; - scrut3 = curHandler !== null; - if (scrut3 === true) { - tmp12 = "\n\twith handler " + curHandler.handler.constructor.name; - tmp13 = msg + tmp12; - msg = tmp13; + scrut1 = curHandler !== null; + if (scrut1 === true) { + tmp6 = "\n\twith handler " + curHandler.handler.constructor.name; + tmp7 = msg + tmp6; + msg = tmp7; atTail = false; - tmp14 = runtime.Unit; + tmp8 = runtime.Unit; } else { - tmp14 = runtime.Unit; + tmp8 = runtime.Unit; } - tmp15 = tmp14; - continue tmp19 + tmp = tmp8; + continue tmp4 } else { - tmp15 = runtime.Unit; + tmp = runtime.Unit; } break; } if (atTail === true) { - tmp16 = msg + "\n\tat tail position"; - msg = tmp16; - tmp17 = runtime.Unit; + tmp1 = msg + "\n\tat tail position"; + msg = tmp1; + tmp2 = runtime.Unit; } else { - tmp17 = runtime.Unit; + tmp2 = runtime.Unit; } - tmp18 = tmp17; + tmp3 = tmp2; } else { - tmp18 = runtime.Unit; + tmp3 = runtime.Unit; } return msg } @@ -749,7 +752,7 @@ let Runtime1; return runtime.safeCall(globalThis.console.log(tmp2)) } static debugContTrace(contTrace) { - let scrut, scrut1, vis, hl, cur, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; + let scrut, scrut1, vis, hl, cur, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12; if (contTrace instanceof Runtime.ContTrace.class) { tmp = globalThis.console.log("resumed: ", contTrace.resumed); scrut = contTrace.last === contTrace; @@ -779,22 +782,23 @@ let Runtime1; tmp9 = Runtime.showFunctionContChain(contTrace.next, hl, vis, 0); tmp10 = runtime.safeCall(globalThis.console.log(tmp9)); cur = contTrace.nextHandler; - tmp15: while (true) { + tmp13: while (true) { + let scrut2, tmp14, tmp15; scrut2 = cur !== null; if (scrut2 === true) { - tmp11 = Runtime.showHandlerContChain(cur, hl, vis, 0); - tmp12 = runtime.safeCall(globalThis.console.log(tmp11)); + tmp14 = Runtime.showHandlerContChain(cur, hl, vis, 0); + tmp15 = runtime.safeCall(globalThis.console.log(tmp14)); cur = cur.nextHandler; - tmp13 = runtime.Unit; - continue tmp15 + tmp11 = runtime.Unit; + continue tmp13 } else { - tmp13 = runtime.Unit; + tmp11 = runtime.Unit; } break; } return runtime.safeCall(globalThis.console.log()) } else { - tmp14 = runtime.safeCall(globalThis.console.log("Not a cont trace:")); + tmp12 = runtime.safeCall(globalThis.console.log("Not a cont trace:")); return runtime.safeCall(globalThis.console.log(contTrace)) } } @@ -836,8 +840,9 @@ let Runtime1; } } static handleEffects(cur) { - let nxt, scrut, tmp, tmp1; - tmp2: while (true) { + let tmp; + tmp1: while (true) { + let nxt, scrut, tmp2; if (cur instanceof Runtime.EffectSig.class) { nxt = Runtime.handleEffect(cur); scrut = cur === nxt; @@ -845,27 +850,28 @@ let Runtime1; return cur } else { cur = nxt; - tmp = runtime.Unit; + tmp2 = runtime.Unit; } - tmp1 = tmp; - continue tmp2 + tmp = tmp2; + continue tmp1 } else { return cur } break; } - return tmp1 + return tmp } static handleEffect(cur) { - let prevHandlerFrame, scrut, scrut1, scrut2, handlerFrame, saved, scrut3, scrut4, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; + let prevHandlerFrame, scrut, handlerFrame, saved, scrut1, scrut2, tmp, tmp1, tmp2, tmp3, tmp4, tmp5; prevHandlerFrame = cur.contTrace; tmp6: while (true) { + let scrut3, scrut4; split_root$: { split_1$: { - scrut = prevHandlerFrame.nextHandler !== null; - if (scrut === true) { - scrut1 = prevHandlerFrame.nextHandler.handler !== cur.handler; - if (scrut1 === true) { + scrut3 = prevHandlerFrame.nextHandler !== null; + if (scrut3 === true) { + scrut4 = prevHandlerFrame.nextHandler.handler !== cur.handler; + if (scrut4 === true) { prevHandlerFrame = prevHandlerFrame.nextHandler; tmp = runtime.Unit; continue tmp6 @@ -880,8 +886,8 @@ let Runtime1; } break; } - scrut2 = prevHandlerFrame.nextHandler === null; - if (scrut2 === true) { + scrut = prevHandlerFrame.nextHandler === null; + if (scrut === true) { return cur } else { tmp1 = runtime.Unit; @@ -896,16 +902,16 @@ let Runtime1; tmp3 = runtime.safeCall(cur.handlerFun(tmp2)); cur = tmp3; if (cur instanceof Runtime.EffectSig.class) { - scrut3 = saved.next !== null; - if (scrut3 === true) { + scrut1 = saved.next !== null; + if (scrut1 === true) { cur.contTrace.last.next = saved.next; cur.contTrace.last = saved.last; tmp4 = runtime.Unit; } else { tmp4 = runtime.Unit; } - scrut4 = saved.nextHandler !== null; - if (scrut4 === true) { + scrut2 = saved.nextHandler !== null; + if (scrut2 === true) { cur.contTrace.lastHandler.nextHandler = saved.nextHandler; cur.contTrace.lastHandler = saved.lastHandler; tmp5 = runtime.Unit; @@ -932,14 +938,15 @@ let Runtime1; } } static resumeContTrace(contTrace, value) { - let cont, handlerCont, curDepth, scrut, scrut1, tmp, tmp1, tmp2, tmp3, tmp4; + let cont, handlerCont, curDepth, tmp; cont = contTrace.next; handlerCont = contTrace.nextHandler; curDepth = Runtime.stackDepth; - tmp5: while (true) { + tmp1: while (true) { + let scrut, scrut1, tmp2, tmp3, tmp4, tmp5; if (cont instanceof Runtime.FunctionContFrame.class) { - tmp = runtime.safeCall(cont.resume(value)); - value = tmp; + tmp2 = runtime.safeCall(cont.resume(value)); + value = tmp2; Runtime.stackDepth = curDepth; if (value instanceof Runtime.EffectSig.class) { value.contTrace.last.next = cont.next; @@ -947,37 +954,37 @@ let Runtime1; scrut = contTrace.last !== cont; if (scrut === true) { value.contTrace.last = contTrace.last; - tmp1 = runtime.Unit; + tmp3 = runtime.Unit; } else { - tmp1 = runtime.Unit; + tmp3 = runtime.Unit; } scrut1 = handlerCont !== null; if (scrut1 === true) { value.contTrace.lastHandler = contTrace.lastHandler; - tmp2 = runtime.Unit; + tmp4 = runtime.Unit; } else { - tmp2 = runtime.Unit; + tmp4 = runtime.Unit; } return value } else { cont = cont.next; - tmp3 = runtime.Unit; + tmp5 = runtime.Unit; } - tmp4 = tmp3; - continue tmp5 + tmp = tmp5; + continue tmp1 } else { if (handlerCont instanceof Runtime.HandlerContFrame.class) { cont = handlerCont.next; handlerCont = handlerCont.nextHandler; - tmp4 = runtime.Unit; - continue tmp5 + tmp = runtime.Unit; + continue tmp1 } else { return value } } break; } - return tmp4 + return tmp } static checkDepth() { let scrut, tmp, lambda; @@ -993,24 +1000,25 @@ let Runtime1; } } static runStackSafe(limit, f) { - let result, scrut, saved, tmp, tmp1; + let result, tmp; Runtime.stackLimit = limit; Runtime.stackDepth = 1; Runtime.stackHandler = Runtime.StackDelayHandler; result = Runtime.enterHandleBlock(Runtime.StackDelayHandler, f); Runtime.stackDepth = 1; - tmp2: while (true) { + tmp1: while (true) { + let scrut, saved, tmp2; scrut = Runtime.stackResume !== null; if (scrut === true) { saved = Runtime.stackResume; Runtime.stackResume = null; - tmp = runtime.safeCall(saved()); - result = tmp; + tmp2 = runtime.safeCall(saved()); + result = tmp2; Runtime.stackDepth = 1; - tmp1 = runtime.Unit; - continue tmp2 + tmp = runtime.Unit; + continue tmp1 } else { - tmp1 = runtime.Unit; + tmp = runtime.Unit; } break; } diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index 49469242a7..77f19c16a9 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -160,11 +160,7 @@ let xs = new Oopsie //│ ╙── ^^^^^^ //│ ╔══[ERROR] Expected a statically known class; found ‹error›. //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'xs' -//│ ╟── which references the symbol introduced here -//│ ║ l.157: let xs = new Oopsie -//│ ╙── ^^ -//│ ═══[RUNTIME ERROR] ReferenceError: xs is not defined +//│ xs = undefined // ——— ——— ——— @@ -183,7 +179,7 @@ class C :sjs class D extends id(C) //│ ╔══[ERROR] Expected a statically known class; found selection. -//│ ║ l.184: class D extends id(C) +//│ ║ l.180: class D extends id(C) //│ ║ ^^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ JS (unsanitized): @@ -211,13 +207,13 @@ let x = 0 :fixme set x += 1; () //│ ╔══[ERROR] Expected a right-hand side for this assignment -//│ ║ l.212: set x += 1; () +//│ ║ l.208: set x += 1; () //│ ╙── ^^^^^^^^^^ :e set (x += 1; ()) //│ ╔══[ERROR] Expected a right-hand side for this assignment -//│ ║ l.218: set (x += 1; ()) +//│ ║ l.214: set (x += 1; ()) //│ ╙── ^^^^^^^^^^^^ (set x += 1); () @@ -225,7 +221,7 @@ set (x += 1; ()) :fixme [x, set x += 1; x] //│ ╔══[ERROR] Expected a right-hand side for this assignment -//│ ║ l.226: [x, set x += 1; x] +//│ ║ l.222: [x, set x += 1; x] //│ ╙── ^^^^^^^^^ //│ = [1] @@ -247,13 +243,13 @@ import "../../mlscript-compile/Stack.mls" // The parser rejects this, but it should be allowed. open Stack { ::, Nil } //│ ╔══[ERROR] Illegal 'open' statement shape. -//│ ║ l.248: open Stack { ::, Nil } +//│ ║ l.244: open Stack { ::, Nil } //│ ╙── ^^^^^^^^^^^^^^^ // Instead, if we parenthesize the operator, it is rejected by the elaborator. open Stack { (::), Nil } //│ ╔══[ERROR] Illegal 'open' statement element. -//│ ║ l.254: open Stack { (::), Nil } +//│ ║ l.250: open Stack { (::), Nil } //│ ╙── ^^^^ open Stack { Nil, :: } @@ -261,7 +257,7 @@ open Stack { Nil, :: } :sjs 1 :: Nil //│ ╔══[ERROR] Module 'Stack' does not contain member '::' -//│ ║ l.262: 1 :: Nil +//│ ║ l.258: 1 :: Nil //│ ╙── ^^ //│ JS (unsanitized): //│ Stack["::"](1, Stack.Nil) @@ -272,9 +268,9 @@ open Stack { Nil, :: } mkStr of ... // hello "oops" //│ ╔══[PARSE ERROR] Expected start of expression in this position; found new line instead -//│ ║ l.272: mkStr of ... // hello +//│ ║ l.268: mkStr of ... // hello //│ ║ ^ -//│ ║ l.273: "oops" +//│ ║ l.269: "oops" //│ ╙── //│ = "oops" @@ -297,25 +293,25 @@ Foo(1, 2, 3).args :todo if Foo(1, 2, 3) is Foo(...args) then args //│ ╔══[ERROR] Unrecognized pattern (spread). -//│ ║ l.298: if Foo(1, 2, 3) is Foo(...args) then args +//│ ║ l.294: if Foo(1, 2, 3) is Foo(...args) then args //│ ╙── ^^^^^^^ //│ ╔══[ERROR] Name not found: args -//│ ║ l.298: if Foo(1, 2, 3) is Foo(...args) then args +//│ ║ l.294: if Foo(1, 2, 3) is Foo(...args) then args //│ ╙── ^^^^ //│ ╔══[ERROR] The constructor does not take any arguments but found one argument. -//│ ║ l.298: if Foo(1, 2, 3) is Foo(...args) then args +//│ ║ l.294: if Foo(1, 2, 3) is Foo(...args) then args //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] Error: match error if Foo(1, 2, 3) is Foo(a, b, c) then [a, b, c] //│ ╔══[ERROR] The constructor does not take any arguments but found three arguments. -//│ ║ l.310: if Foo(1, 2, 3) is Foo(a, b, c) then [a, b, c] +//│ ║ l.306: if Foo(1, 2, 3) is Foo(a, b, c) then [a, b, c] //│ ╙── ^^^^^^^ //│ ═══[RUNTIME ERROR] Error: match error if Foo(1, 2, 3) is Foo(arg) then arg //│ ╔══[ERROR] The constructor does not take any arguments but found one argument. -//│ ║ l.316: if Foo(1, 2, 3) is Foo(arg) then arg +//│ ║ l.312: if Foo(1, 2, 3) is Foo(arg) then arg //│ ╙── ^^^ //│ ═══[RUNTIME ERROR] Error: match error @@ -349,10 +345,10 @@ fun foo() = let bar(x: Str): Str = x + x bar("test") //│ ╔══[ERROR] Unsupported let binding shape -//│ ║ l.349: let bar(x: Str): Str = x + x +//│ ║ l.345: let bar(x: Str): Str = x + x //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[ERROR] Expected 0 arguments, got 1 -//│ ║ l.350: bar("test") +//│ ║ l.346: bar("test") //│ ╙── ^^^^^^^^ // ——— ——— ——— @@ -362,7 +358,7 @@ fun foo() = module Foo(x) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ╟── which references the symbol introduced here -//│ ║ l.362: module Foo(x) +//│ ║ l.358: module Foo(x) //│ ╙── ^ //│ JS (unsanitized): //│ let Foo5; @@ -386,7 +382,7 @@ module Foo(x) module Foo(val x) //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' //│ ╟── which references the symbol introduced here -//│ ║ l.386: module Foo(val x) +//│ ║ l.382: module Foo(val x) //│ ╙── ^ //│ JS (unsanitized): //│ let Foo7; @@ -409,7 +405,7 @@ module Foo(val x) // TODO support syntax? data class Foo(mut x) //│ ╔══[ERROR] Expected a valid parameter, found 'mut'-modified identifier -//│ ║ l.410: data class Foo(mut x) +//│ ║ l.406: data class Foo(mut x) //│ ╙── ^ // ——— ——— ——— @@ -426,10 +422,10 @@ set this.pc = 0 set (this).pc = 0 //│ ╔══[ERROR] Cannot use 'this' outside of an object scope. -//│ ║ l.425: set this.pc = 0 +//│ ║ l.421: set this.pc = 0 //│ ╙── ^^^^ //│ ╔══[ERROR] Cannot use 'this' outside of an object scope. -//│ ║ l.427: (this).pc = 0 +//│ ║ l.423: (this).pc = 0 //│ ╙── ^^^^ // But this doesn't... @@ -438,10 +434,10 @@ set set this.pc = 0 //│ ╔══[PARSE ERROR] Unexpected static selector here -//│ ║ l.439: this.pc = 0 +//│ ║ l.435: this.pc = 0 //│ ╙── ^^^ //│ ╔══[ERROR] Expected a right-hand side for this assignment -//│ ║ l.439: this.pc = 0 +//│ ║ l.435: this.pc = 0 //│ ╙── ^^^^ // ——— ——— ——— @@ -453,7 +449,7 @@ pattern A(pattern B) = B fun foo(x) = if x is @annotations.compile A(0) as y then y //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//│ ║ l.454: fun foo(x) = if x is @annotations.compile A(0) as y then y +//│ ║ l.450: fun foo(x) = if x is @annotations.compile A(0) as y then y //│ ╙── ^ // ——— ——— ——— diff --git a/hkmc2/shared/src/test/mlscript/basics/Drop.mls b/hkmc2/shared/src/test/mlscript/basics/Drop.mls index 9d957653bc..cce678bd28 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Drop.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Drop.mls @@ -22,7 +22,10 @@ drop { a: 0, b: 1 } :sjs let discard = drop _ //│ JS (unsanitized): -//│ let discard, discard1; discard1 = function discard(_0) { return runtime.Unit }; discard = discard1; +//│ let discard; +//│ let discard1; +//│ discard1 = function discard(_0) { return runtime.Unit }; +//│ discard = discard1; //│ discard = fun discard discard of { a: 0, b: 1 } diff --git a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls index c22fcf695c..39b9941e38 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MemberProjections.mls @@ -46,7 +46,7 @@ M.Foo:: n(foo, 2) :sjs let m = M.Foo::m //│ JS (unsanitized): -//│ let m, m1; m1 = function m(self, ...args) { return self.m(...args) }; m = m1; +//│ let m; let m1; m1 = function m(self, ...args) { return self.m(...args) }; m = m1; //│ m = fun m m(foo) diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls index 2c3b5349e7..c76804e25b 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls @@ -77,7 +77,8 @@ Foo(1, 2) :sjs let f = Foo // should compile to `(x, y) => Foo(x, y)(use[Int])` //│ JS (unsanitized): -//│ let f, f1; +//│ let f; +//│ let f1; //│ f1 = function f(x, y) { //│ let tmp4; //│ tmp4 = Foo7(x, y); diff --git a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls index 99fe9b0fdb..72c154d117 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls @@ -109,7 +109,7 @@ passTo(1, add(., 1) @ _ + _)(2) :sjs let f = add(_, 1) //│ JS (unsanitized): -//│ let f, f1; f1 = function f(_0) { let tmp15; tmp15 = add(); return tmp15(_0, 1) }; f = f1; +//│ let f; let f1; f1 = function f(_0) { let tmp15; tmp15 = add(); return tmp15(_0, 1) }; f = f1; //│ f = fun f :fixme diff --git a/hkmc2/shared/src/test/mlscript/basics/Records.mls b/hkmc2/shared/src/test/mlscript/basics/Records.mls index c4d757b8aa..a60b1f3527 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Records.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Records.mls @@ -78,11 +78,7 @@ let rcd = //│ ╔══[ERROR] Name not found: bar //│ ║ l.73: bar = //│ ╙── ^^^ -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'rcd' -//│ ╟── which references the symbol introduced here -//│ ║ l.71: let rcd = -//│ ╙── ^^^ -//│ rcd = {foo: 1, bar: "..."} +//│ rcd = undefined // * How about this syntax? @@ -93,10 +89,10 @@ let rcd = new with bar = "..." //│ ╔══[ERROR] Name not found: foo -//│ ║ l.92: foo = 1 +//│ ║ l.88: foo = 1 //│ ╙── ^^^ //│ ╔══[ERROR] Name not found: bar -//│ ║ l.93: bar = +//│ ║ l.89: bar = //│ ╙── ^^^ //│ rcd = $anon @@ -138,7 +134,7 @@ m0.get(41) :e {get(k): _} //│ ╔══[ERROR] Unexpected record key shape. -//│ ║ l.139: {get(k): _} +//│ ║ l.135: {get(k): _} //│ ╙── ^^^^^^ //│ = fun @@ -193,14 +189,14 @@ display(x: 88, y: 99, i: 0) :todo t("group") //│ ╔══[ERROR] Expected 2 arguments, got 1 -//│ ║ l.194: t("group") +//│ ║ l.190: t("group") //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 't' expected 2 arguments but got 1 :todo t("group") //│ ╔══[ERROR] Expected 2 arguments, got 1 -//│ ║ l.201: t("group") +//│ ║ l.197: t("group") //│ ╙── ^^^^^^^^^ //│ ═══[RUNTIME ERROR] Error: Function 't' expected 2 arguments but got 1 diff --git a/hkmc2/shared/src/test/mlscript/basics/Underscores.mls b/hkmc2/shared/src/test/mlscript/basics/Underscores.mls index 593a13bc38..198b5ecd14 100644 --- a/hkmc2/shared/src/test/mlscript/basics/Underscores.mls +++ b/hkmc2/shared/src/test/mlscript/basics/Underscores.mls @@ -11,17 +11,13 @@ _ //│ = fun :e -:ge -:re +// :ge +// :re y: _ //│ ╔══[ERROR] Illegal position for '_' placeholder. //│ ║ l.16: y: _ //│ ╙── ^ -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//│ ╟── which references the symbol introduced here -//│ ║ l.16: y: _ -//│ ╙── ^ -//│ ═══[RUNTIME ERROR] ReferenceError: y is not defined +//│ y = undefined (y: _) //│ = fun @@ -65,7 +61,8 @@ print(_) :sjs let test = _.f(0, _, 2) //│ JS (unsanitized): -//│ let test, test1; +//│ let test; +//│ let test1; //│ test1 = function test(_0, _1) { return runtime.safeCall(_0.f(0, _1, 2)) }; //│ test = test1; //│ test = fun test @@ -139,17 +136,13 @@ mkObj(1, 2) :re let mkObj = x: _, y: _, z: 3 //│ ╔══[ERROR] Illegal position for '_' placeholder. -//│ ║ l.140: let mkObj = x: _, y: _, z: 3 +//│ ║ l.137: let mkObj = x: _, y: _, z: 3 //│ ╙── ^ //│ mkObj = fun mkObj -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'y' -//│ ╟── which references the symbol introduced here -//│ ║ l.140: let mkObj = x: _, y: _, z: 3 -//│ ╙── ^ -//│ ═══[RUNTIME ERROR] ReferenceError: y is not defined +//│ y = undefined //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'z' //│ ╟── which references the symbol introduced here -//│ ║ l.140: let mkObj = x: _, y: _, z: 3 +//│ ║ l.137: let mkObj = x: _, y: _, z: 3 //│ ╙── ^ //│ ═══[RUNTIME ERROR] ReferenceError: z is not defined diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index 8b0b2ecff8..5e637105b7 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -262,7 +262,8 @@ region x in x.ref 42 :sjs region x in let y = x.ref 42 in !(y) //│ JS (unsanitized): -//│ let x4, y; +//│ let y; +//│ let x4; //│ x4 = globalThis.Object.freeze(new globalThis.Region()); //│ y = globalThis.Object.freeze(new globalThis.Ref(x4, 42)); //│ y.value @@ -273,7 +274,8 @@ region x in let y = x.ref 42 in !(y) :sjs region x in let y = x.ref 42 in y := 0 //│ JS (unsanitized): -//│ let x5, y1; +//│ let y1; +//│ let x5; //│ x5 = globalThis.Object.freeze(new globalThis.Region()); //│ y1 = globalThis.Object.freeze(new globalThis.Ref(x5, 42)); //│ y1.value = 0; diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls index d72caafeeb..48f541fb92 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbGetters.mls @@ -91,7 +91,8 @@ fun test2() = //│ test22 = function test2() { //│ let funny, tmp1; //│ funny = function funny() { -//│ let lambda, lambda1; +//│ let lambda; +//│ let lambda1; //│ lambda = (undefined, function (caseScrut) { //│ if (caseScrut === 0) { //│ return 0 @@ -130,7 +131,7 @@ fun test2() = fun test3 = print("Hi") //│ ╔══[ERROR] Function definition shape not yet supported for test3 -//│ ║ l.131: print("Hi") +//│ ║ l.132: print("Hi") //│ ╙── ^^^^^^^^^^^ //│ Type: ⊤ diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbPrelude.mls b/hkmc2/shared/src/test/mlscript/bbml/bbPrelude.mls index 2b019cf03a..c974756539 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbPrelude.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbPrelude.mls @@ -88,6 +88,9 @@ declare module annotations with object buffered object bufferable +declare module scope with + fun locally + declare fun run: [T] -> CodeBase[out T, out Nothing, out Any] -> T declare fun log: Str -> Any diff --git a/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls b/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls index a2ed080be2..7bf650836b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls @@ -24,11 +24,8 @@ //│ Lowered: //│ Program: //│ imports = Nil -//│ main = Assign: -//│ lhs = $block$res -//│ rhs = Lit of IntLit of 2 -//│ rest = Return: \ -//│ res = Lit of UnitLit of false +//│ main = Return: +//│ res = Lit of IntLit of 2 //│ implct = true //│ = 2 @@ -46,24 +43,23 @@ print("Hi") //│ Lowered: //│ Program: //│ imports = Nil -//│ main = Assign: -//│ lhs = $tmp -//│ rhs = Call: -//│ fun = Select{sym=term:module:Predef.print}: -//│ qual = Ref{disamb=module:Predef}: -//│ l = member:Predef -//│ disamb = S of module:Predef -//│ name = Ident of "print" -//│ args = Ls of -//│ Arg: -//│ spread = N -//│ value = Lit of StrLit of "Hi" -//│ rest = Assign: \ -//│ lhs = $block$res -//│ rhs = Lit of IntLit of 2 -//│ rest = Return: \ -//│ res = Lit of UnitLit of false -//│ implct = true +//│ main = Scoped: +//│ syms = HashSet($tmp) +//│ body = Assign: +//│ lhs = $tmp +//│ rhs = Call: +//│ fun = Select{sym=term:module:Predef.print}: +//│ qual = Ref{disamb=module:Predef}: +//│ l = member:Predef +//│ disamb = S of module:Predef +//│ name = Ident of "print" +//│ args = Ls of +//│ Arg: +//│ spread = N +//│ value = Lit of StrLit of "Hi" +//│ rest = Return: \ +//│ res = Lit of IntLit of 2 +//│ implct = true //│ > Hi //│ = 2 diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index a4fddd8d26..1c42627d83 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -6,7 +6,7 @@ let x //│ JS (unsanitized): -//│ let x; x = undefined; +//│ let x; //│ x = undefined x = 1 @@ -56,7 +56,7 @@ f let f f(x) = x + 1 //│ JS (unsanitized): -//│ let f1, f2; f2 = function f(x1) { return x1 + 1 }; f1 = f2; +//│ let f1; let f2; f2 = function f(x1) { return x1 + 1 }; f1 = f2; //│ f = fun f f(1) @@ -81,7 +81,6 @@ else foo = 1 //│ ╙── ^ //│ JS (unsanitized): //│ let foo1, scrut; -//│ foo1 = undefined; //│ scrut = true; //│ if (scrut === true) { //│ foo1 @@ -96,7 +95,6 @@ else foo = 1 //│ JS (unsanitized): //│ let foo2, scrut1; -//│ foo2 = undefined; //│ scrut1 = true; //│ if (scrut1 === true) { foo2 = 0; runtime.Unit } else { foo2 = 1; runtime.Unit } //│ foo = 0 @@ -120,7 +118,7 @@ foo :fixme fun f() = foo = 0 //│ ╔══[PARSE ERROR] Expected end of input; found '=' keyword instead -//│ ║ l.121: fun f() = foo = 0 +//│ ║ l.119: fun f() = foo = 0 //│ ╙── ^ //│ JS (unsanitized): //│ let f4; f4 = function f() { return foo2 }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls index dd62328b18..b5b50aca43 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls @@ -99,53 +99,52 @@ case //│ sign = N //│ modulefulness = Modulefulness of N //│ restParam = N -//│ body = Match: -//│ scrut = Ref: -//│ l = caseScrut -//│ disamb = N -//│ arms = Ls of -//│ Tuple2: -//│ _1 = Cls: -//│ cls = class:Foo -//│ path = Ref{disamb=class:Foo}: -//│ l = member:Foo -//│ disamb = S of class:Foo -//│ _2 = Assign: -//│ lhs = $argument0$ -//│ rhs = Select{sym=term:class:Foo.x}: -//│ qual = Ref: -//│ l = caseScrut +//│ body = Scoped: +//│ syms = HashSet(a, $argument0$) +//│ body = Match: +//│ scrut = Ref: +//│ l = caseScrut +//│ disamb = N +//│ arms = Ls of +//│ Tuple2: +//│ _1 = Cls: +//│ cls = class:Foo +//│ path = Ref{disamb=class:Foo}: +//│ l = member:Foo +//│ disamb = S of class:Foo +//│ _2 = Assign: +//│ lhs = $argument0$ +//│ rhs = Select{sym=term:class:Foo.x}: +//│ qual = Ref: +//│ l = caseScrut +//│ disamb = N +//│ name = Ident of "x" +//│ rest = Assign: \ +//│ lhs = a +//│ rhs = Ref: +//│ l = $argument0$ //│ disamb = N -//│ name = Ident of "x" -//│ rest = Assign: \ -//│ lhs = a -//│ rhs = Ref: -//│ l = $argument0$ -//│ disamb = N -//│ rest = Return: \ -//│ res = Ref: -//│ l = a -//│ disamb = N -//│ implct = false -//│ dflt = S of Throw of Instantiate: -//│ mut = false -//│ cls = Select: -//│ qual = Ref{disamb=globalThis:globalThis}: -//│ l = globalThis:globalThis -//│ disamb = S of globalThis:globalThis -//│ name = Ident of "Error" -//│ args = Ls of -//│ Arg: -//│ spread = N -//│ value = Lit of StrLit of "match error" -//│ rest = End of "" -//│ rest = Assign: \ -//│ lhs = $block$res -//│ rhs = Ref{disamb=term:lambda}: +//│ rest = Return: \ +//│ res = Ref: +//│ l = a +//│ disamb = N +//│ implct = false +//│ dflt = S of Throw of Instantiate: +//│ mut = false +//│ cls = Select: +//│ qual = Ref{disamb=globalThis:globalThis}: +//│ l = globalThis:globalThis +//│ disamb = S of globalThis:globalThis +//│ name = Ident of "Error" +//│ args = Ls of +//│ Arg: +//│ spread = N +//│ value = Lit of StrLit of "match error" +//│ rest = End of "" +//│ rest = Return: \ +//│ res = Ref{disamb=term:lambda}: //│ l = member:lambda //│ disamb = S of term:lambda -//│ rest = Return: \ -//│ res = Lit of UnitLit of false //│ implct = true //│ = fun diff --git a/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls b/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls index 85ea77ef62..c23fc6c9b4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Formatting.mls @@ -7,7 +7,10 @@ let discard = drop _ //│ JS (unsanitized): -//│ let discard, discard1; discard1 = function discard(_0) { return runtime.Unit }; discard = discard1; +//│ let discard; +//│ let discard1; +//│ discard1 = function discard(_0) { return runtime.Unit }; +//│ discard = discard1; //│ discard = fun discard diff --git a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls index d09f692fa3..37c02239e7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Getters.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Getters.mls @@ -174,7 +174,8 @@ fun baz() = //│ JS (unsanitized): //│ let baz; //│ baz = function baz() { -//│ let w, z, lambda; +//│ let w, z; +//│ let lambda; //│ w = function w() { //│ return 1 //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls index 07e732ff24..ff3242065b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Hygiene.mls @@ -62,7 +62,7 @@ let f = () => x let x = 2 f() //│ JS (unsanitized): -//│ let x, f, x1, f1; x = 1; f1 = function f() { return x }; f = f1; x1 = 2; runtime.safeCall(f()) +//│ let x, f, x1; let f1; x = 1; f1 = function f() { return x }; f = f1; x1 = 2; runtime.safeCall(f()) //│ = 1 //│ f = fun f //│ x = 2 diff --git a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls index 5c783aded4..4cb31f4293 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/IfThenElse.mls @@ -9,7 +9,8 @@ if true then 1 else 0 :sjs let f = x => if x then print("ok") else print("ko") //│ JS (unsanitized): -//│ let f, f1; +//│ let f; +//│ let f1; //│ f1 = function f(x) { //│ if (x === true) { return Predef.print("ok") } else { return Predef.print("ko") } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls b/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls new file mode 100644 index 0000000000..f85db38886 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/NestedScoped.mls @@ -0,0 +1,292 @@ +:js + + +:lot +:sjs +scope.locally of 42 +//│ JS (unsanitized): +//│ { 42 } +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Begin: +//│ sub = End of "" +//│ rest = Scoped: \ +//│ syms = HashSet() +//│ body = Return: +//│ res = Lit of IntLit of 42 +//│ implct = true +//│ = 42 + + +:lot +:sjs +scope.locally of + let x = 42 in x +//│ JS (unsanitized): +//│ { let x; x = 42; x } +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Begin: +//│ sub = End of "" +//│ rest = Scoped: \ +//│ syms = HashSet(x) +//│ body = Assign: +//│ lhs = x +//│ rhs = Lit of IntLit of 42 +//│ rest = Return: \ +//│ res = Ref: +//│ l = x +//│ disamb = N +//│ implct = true +//│ = 42 + + +:lot +:sjs +scope.locally of ( + let x = 42 + x +) +//│ JS (unsanitized): +//│ { let x1; x1 = 42; x1 } +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Begin: +//│ sub = End of "" +//│ rest = Scoped: \ +//│ syms = HashSet(x) +//│ body = Assign: +//│ lhs = x +//│ rhs = Lit of IntLit of 42 +//│ rest = Return: \ +//│ res = Ref: +//│ l = x +//│ disamb = N +//│ implct = true +//│ = 42 + + +:lot +:sjs +scope.locally of scope.locally of ( + let x = 42 + x +) +//│ JS (unsanitized): +//│ {{ let x2; x2 = 42; x2 } } +//│ Lowered: +//│ Program: +//│ imports = Nil +//│ main = Begin: +//│ sub = End of "" +//│ rest = Scoped: \ +//│ syms = HashSet() +//│ body = Begin: +//│ sub = End of "" +//│ rest = Scoped: \ +//│ syms = HashSet(x) +//│ body = Assign: +//│ lhs = x +//│ rhs = Lit of IntLit of 42 +//│ rest = Return: \ +//│ res = Ref: +//│ l = x +//│ disamb = N +//│ implct = true +//│ = 42 + + + +:e +x +//│ ╔══[ERROR] Name not found: x +//│ ║ l.104: x +//│ ╙── ^ + + +:ssjs +fun foo(x) = + let z = 1 + scope.locally of + let y = 1 in x + y + z +//│ JS: +//│ foo = function foo(...args) { +//│ runtime.checkArgs("foo", 1, true, args.length); +//│ let x3 = args[0]; +//│ let z; +//│ z = 1;{ let y, tmp; y = 1; tmp = x3 + y; return tmp + z } +//│ }; +//│ block$res6 = undefined; + + +foo(1) +//│ = 3 + + +:sjs +fun f() = + if true then + scope.locally of ( + let a = 4 + a + 4 + ) + else + let b = 5 + b + 9 +//│ JS (unsanitized): +//│ let f; +//│ f = function f() { +//│ let scrut, b; +//│ scrut = true; +//│ if (scrut === true) {{ let a; a = 4; return a + 4 } } else { b = 5; return b + 9 } +//│ }; + + +f() +//│ = 8 + + +:sjs +fun f() = + if false then + let a = 4 + a + 4 + else + scope.locally of ( + let b = 5 + b + 9 + ) +//│ JS (unsanitized): +//│ let f1; +//│ f1 = function f() { +//│ let scrut, a; +//│ scrut = false; +//│ if (scrut === true) { a = 4; return a + 4 } else {{ let b; b = 5; return b + 9 } } +//│ }; + + +f() +//│ = 14 + + +:sjs +fun g(x, y, z) = + 3 + 4 * if + x then 0 + y then 0 + z then + scope.locally of ( + let a = 1 + (_ + a) + ) +//│ JS (unsanitized): +//│ let g; +//│ g = function g(x3, y, z) { +//│ let tmp, tmp1; +//│ let lambda; +//│ split_root$: { +//│ split_1$: { +//│ if (x3 === true) { +//│ break split_1$ +//│ } else { +//│ if (y === true) { +//│ break split_1$ +//│ } else { +//│ if (z === true) { +//│ let a; +//│ a = 1; +//│ lambda = (undefined, function (_0) { +//│ return _0 + a +//│ }); +//│ tmp = lambda; +//│ break split_root$ +//│ } else { +//│ throw globalThis.Object.freeze(new globalThis.Error("match error")) +//│ } +//│ } +//│ } +//│ } +//│ tmp = 0; +//│ } +//│ tmp1 = 4 * tmp; +//│ return 3 + tmp1 +//│ }; + + + +:sjs +fun foo(x, y) = + let z = [1, 2, 3] + if not x > z.0 do + print("rua") + let m = x + y + scope.locally of ( + let w1 = z.1 + m + let w2 = z.2 - m + if w1 == w2 do + print("ruarua") + ) +//│ JS (unsanitized): +//│ let foo1; +//│ foo1 = function foo(x3, y) { +//│ let z, scrut, m, tmp, tmp1; +//│ z = globalThis.Object.freeze([ +//│ 1, +//│ 2, +//│ 3 +//│ ]); +//│ tmp = x3 > z[0]; +//│ scrut = ! tmp; +//│ if (scrut === true) { +//│ tmp1 = Predef.print("rua"); +//│ } else { +//│ tmp1 = runtime.Unit; +//│ } +//│ m = x3 + y;{ +//│ let w1, w2, scrut1; +//│ w1 = z[1] + m; +//│ w2 = z[2] - m; +//│ scrut1 = Predef.equals(w1, w2); +//│ if (scrut1 === true) { return Predef.print("ruarua") } else { return runtime.Unit } +//│ } +//│ }; + + +:ge +scope.locally() +//│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. +//│ ║ l.259: scope.locally() +//│ ╙── ^^^^^^^^^^^^^ + + +:ge +scope.locally of (let x = 1 in x), (let y = 2 in y) +//│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. +//│ ║ l.266: scope.locally of (let x = 1 in x), (let y = 2 in y) +//│ ╙── ^^^^^^^^^^^^^ + + +:e +:ge +scope.locally of (let x = 1 in x), (let y = 2 in x) +//│ ╔══[ERROR] Name not found: x +//│ ║ l.274: scope.locally of (let x = 1 in x), (let y = 2 in x) +//│ ╙── ^ +//│ ╔══[COMPILATION ERROR] Unsupported form for scope.locally. +//│ ║ l.274: scope.locally of (let x = 1 in x), (let y = 2 in x) +//│ ╙── ^^^^^^^^^^^^^ + + +:sjs +let z = 114 +scope.locally of ( + let y = 514 + y +) +//│ JS (unsanitized): +//│ let z; z = 114;{ let y; y = 514; y } +//│ = 514 +//│ z = 114 diff --git a/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls b/hkmc2/shared/src/test/mlscript/codegen/PartialApps.mls index f206a5bf8d..7abc9aa1fd 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 f2, f3; f3 = function f(_0, _1) { return foo(1, _0, _1) }; f2 = f3; +//│ let f2; let f3; f3 = function f(_0, _1) { return foo(1, _0, _1) }; f2 = f3; //│ f = fun f f(2, 3) @@ -79,7 +79,7 @@ h(1) :sjs let i = _(0, 1, _) //│ JS (unsanitized): -//│ let i, i1; i1 = function i(_0, _1) { return runtime.safeCall(_0(0, 1, _1)) }; i = i1; +//│ let i; let i1; i1 = function i(_0, _1) { return runtime.safeCall(_0(0, 1, _1)) }; i = i1; //│ i = fun i i(print, 2) diff --git a/hkmc2/shared/src/test/mlscript/codegen/Quasiquotes.mls b/hkmc2/shared/src/test/mlscript/codegen/Quasiquotes.mls index b399a24fff..3c1db6eeb8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Quasiquotes.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Quasiquotes.mls @@ -76,12 +76,13 @@ x `=> print(x) x //│ JS (unsanitized): -//│ let x1, tmp6, tmp7, tmp8, arr2; -//│ tmp6 = globalThis.Object.freeze(new Term.Symbol("x")); -//│ x1 = globalThis.Object.freeze(new Term.Ref(tmp6)); -//│ tmp7 = Predef.print(x1); +//│ let tmp6; +//│ let x1, tmp7, tmp8, arr2; +//│ tmp7 = globalThis.Object.freeze(new Term.Symbol("x")); +//│ x1 = globalThis.Object.freeze(new Term.Ref(tmp7)); +//│ tmp6 = Predef.print(x1); //│ arr2 = globalThis.Object.freeze([ -//│ tmp6 +//│ tmp7 //│ ]); //│ tmp8 = x1; //│ globalThis.Object.freeze(new Term.Lam(arr2, tmp8)) diff --git a/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls index 98b3d35d04..69dc697eb6 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/RandomStuff.mls @@ -6,8 +6,8 @@ fun foo() = if false do foo() //│ JS (unsanitized): //│ let foo; //│ foo = function foo() { -//│ let scrut; //│ loopLabel: while (true) { +//│ let scrut; //│ scrut = false; //│ if (scrut === true) { continue loopLabel } else { return runtime.Unit } //│ break; @@ -68,7 +68,7 @@ do //│ JS (unsanitized): //│ let f, f1; //│ f1 = 1; -//│ f = function f() { loopLabel: while (true) { continue loopLabel; break; } }; +//│ f = function f() { loopLabel: while (true) { let tmp; continue loopLabel; break; } }; //│ runtime.Unit //│ f = 1 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls index 241505d047..9fad84082a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Repl.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Repl.mls @@ -10,7 +10,7 @@ fun res() = 1 //│ REPL> Sending: block$res3 = undefined //│ REPL> Collected: //│ > undefined -//│ REPL> Sending: let res;try { res = function res(...args) { runtime.checkArgs("res", 0, true, args.length); return 1 }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: let res2;try { res2 = function res(...args) { runtime.checkArgs("res", 0, true, args.length); return 1 }; block$res3 = undefined; } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: @@ -47,7 +47,7 @@ res //│ REPL> Sending: block$res5 = undefined //│ REPL> Collected: //│ > undefined -//│ REPL> Sending: try { block$res5 = res; undefined } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } +//│ REPL> Sending: try { block$res5 = res2; undefined } catch (e) { console.log('\u200B' + e.stack + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: diff --git a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls index ea32941035..0a5d4e6224 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SanityChecks.mls @@ -69,7 +69,11 @@ id(f)(3)(4) :noSanityCheck let f = (x, y) => x + y in f(2) //│ JS: -//│ f4 = function f(x, y) { return x + y }; f3 = f4; block$res5 = runtime.safeCall(f3(2)); undefined +//│ let f4; +//│ f4 = function f(x, y) { return x + y }; +//│ f3 = f4; +//│ block$res5 = runtime.safeCall(f3(2)); +//│ undefined //│ = NaN :ssjs @@ -77,6 +81,7 @@ let f = (x, y) => x + y in f(2) let f = (x, y) => x + y f(2) //│ JS: +//│ let f6; //│ f6 = function f(...args) { //│ runtime.checkArgs("f", 2, true, args.length); //│ let x = args[0]; diff --git a/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocks.mls b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocks.mls new file mode 100644 index 0000000000..12e5c2e0b8 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocks.mls @@ -0,0 +1,461 @@ +:js + + +:sjs +let x = + let y = 1 + 3 * 4 + y + 4 +//│ JS (unsanitized): +//│ let x, y, tmp; tmp = 3 * 4; y = 1 + tmp; x = y + 4; +//│ x = 17 + + +:sjs +fun f() = + if true then + let a = 4 + a + 4 + else + let b = 5 + b + 9 +//│ JS (unsanitized): +//│ let f; +//│ f = function f() { +//│ let scrut, a, b; +//│ scrut = true; +//│ if (scrut === true) { a = 4; return a + 4 } else { b = 5; return b + 9 } +//│ }; + + +// TODO: improve later: `let a` can be moved to the branch body +// TODO: `lambda` is not currently included in the symbols in the `Scoped` block (because this is created in `LambdaRewriter`, a later pass) +// NOTE: `tmp1` and `tmp2` should indeed be declared in the top level of the function body +:sjs +fun g(x, y, z) = + 3 + 4 * if + x then 0 + y then 0 + z then + let a = 1 + (_ + a) +//│ JS (unsanitized): +//│ let g; +//│ g = function g(x1, y1, z) { +//│ let a, tmp1, tmp2; +//│ let lambda; +//│ split_root$: { +//│ split_1$: { +//│ if (x1 === true) { +//│ break split_1$ +//│ } else { +//│ if (y1 === true) { +//│ break split_1$ +//│ } else { +//│ if (z === true) { +//│ a = 1; +//│ lambda = (undefined, function (_0) { +//│ return _0 + a +//│ }); +//│ tmp1 = lambda; +//│ break split_root$ +//│ } else { +//│ throw globalThis.Object.freeze(new globalThis.Error("match error")) +//│ } +//│ } +//│ } +//│ } +//│ tmp1 = 0; +//│ } +//│ tmp2 = 4 * tmp1; +//│ return 3 + tmp2 +//│ }; + + +module M +data class A(a, b) +data class B(a, b) +data object Nil + + + +:sjs +fun f() = if x is + A(a, b) and a > b then true + B(c, d) then false +//│ JS (unsanitized): +//│ let f1; +//│ f1 = function f() { +//│ let a, b, scrut, c, d, argument0$, argument1$, tmp1; +//│ split_root$: { +//│ split_default$: { +//│ if (x instanceof A1.class) { +//│ argument0$ = x.a; +//│ argument1$ = x.b; +//│ b = argument1$; +//│ a = argument0$; +//│ scrut = a > b; +//│ if (scrut === true) { +//│ tmp1 = true; +//│ break split_root$ +//│ } else { +//│ break split_default$ +//│ } +//│ } else if (x instanceof B1.class) { +//│ argument0$ = x.a; +//│ argument1$ = x.b; +//│ d = argument1$; +//│ c = argument0$; +//│ tmp1 = false; +//│ break split_root$ +//│ } else { +//│ break split_default$ +//│ } +//│ } +//│ throw globalThis.Object.freeze(new globalThis.Error("match error")); +//│ } +//│ return tmp1 +//│ }; + + +:sjs +fun f() = if A(1, Nil) is + A(1, Nil) then 3 +//│ JS (unsanitized): +//│ let f2; +//│ f2 = function f() { +//│ let scrut, argument0$, argument1$, tmp1; +//│ split_root$: { +//│ split_default$: { +//│ scrut = A1(1, Nil1); +//│ if (scrut instanceof A1.class) { +//│ argument0$ = scrut.a; +//│ argument1$ = scrut.b; +//│ if (argument0$ === 1) { +//│ if (argument1$ instanceof Nil1.class) { +//│ tmp1 = 3; +//│ break split_root$ +//│ } else { +//│ break split_default$ +//│ } +//│ } else { +//│ break split_default$ +//│ } +//│ } else { +//│ break split_default$ +//│ } +//│ } +//│ throw globalThis.Object.freeze(new globalThis.Error("match error")); +//│ } +//│ return tmp1 +//│ }; + + + +:sjs +fun f(x) = + x = while x is + A(a, _) then a() + B(a, _) then a() + else + x() + x +//│ JS (unsanitized): +//│ let f3; +//│ f3 = function f(x1) { +//│ let tmp1; +//│ tmp2: while (true) { +//│ let a, a1, argument0$, argument1$; +//│ if (x1 instanceof A1.class) { +//│ argument0$ = x1.a; +//│ argument1$ = x1.b; +//│ a = argument0$; +//│ tmp1 = runtime.safeCall(a()); +//│ continue tmp2 +//│ } else if (x1 instanceof B1.class) { +//│ argument0$ = x1.a; +//│ argument1$ = x1.b; +//│ a1 = argument0$; +//│ tmp1 = runtime.safeCall(a1()); +//│ continue tmp2 +//│ } else { tmp1 = runtime.safeCall(x1()); } +//│ break; +//│ } +//│ x1 = tmp1; +//│ return x1 +//│ }; + + + +:sjs +fun f() = + let y = x + 1 + while y is + A(z, a) and + false and true do + set y = z + 1 + else + y = a +//│ JS (unsanitized): +//│ let f4; +//│ f4 = function f() { +//│ let y1, tmp1; +//│ y1 = x + 1; +//│ tmp2: while (true) { +//│ let a, z, scrut, scrut1, argument0$, argument1$, tmp3; +//│ split_root$: { +//│ split_1$: { +//│ if (y1 instanceof A1.class) { +//│ argument0$ = y1.a; +//│ argument1$ = y1.b; +//│ a = argument1$; +//│ z = argument0$; +//│ scrut = false; +//│ if (scrut === true) { +//│ scrut1 = true; +//│ if (scrut1 === true) { +//│ tmp3 = z + 1; +//│ y1 = tmp3; +//│ tmp1 = runtime.Unit; +//│ continue tmp2 +//│ } else { +//│ break split_1$ +//│ } +//│ } else { +//│ break split_1$ +//│ } +//│ } else { tmp1 = runtime.Unit; } +//│ } +//│ y1 = a; +//│ tmp1 = runtime.Unit; +//│ } +//│ break; +//│ } +//│ return tmp1 +//│ }; + + +:sjs +fun f(x, y) = + while x && y do + f(0, 0) + 4 +//│ JS (unsanitized): +//│ let f5; +//│ f5 = function f(x1, y1) { +//│ let tmp1; +//│ tmp2: while (true) { +//│ let scrut, lambda, tmp3; +//│ lambda = (undefined, function () { +//│ return y1 +//│ }); +//│ scrut = runtime.short_and(x1, lambda); +//│ if (scrut === true) { +//│ tmp3 = f5(0, 0); +//│ tmp1 = tmp3 + 4; +//│ continue tmp2 +//│ } else { tmp1 = runtime.Unit; } +//│ break; +//│ } +//│ return tmp1 +//│ }; + + + +:sjs +class A with + val x = 1 + 2 +//│ JS (unsanitized): +//│ let A3; +//│ (class A2 { +//│ static { +//│ A3 = this +//│ } +//│ constructor() { +//│ let tmp1; +//│ tmp1 = 1 + 2; +//│ this.x = tmp1; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A"]; +//│ }); + + +let state = 0 +//│ state = 0 + +:sjs +class Foo() with // the class definition is moved to the location of its companion + fun foo() = state +set state += 1 +module Foo with + val res = Foo().foo() +//│ JS (unsanitized): +//│ let Foo1, tmp1; +//│ tmp1 = state + 1; +//│ state = tmp1; +//│ Foo1 = function Foo() { +//│ return globalThis.Object.freeze(new Foo.class()); +//│ }; +//│ (class Foo { +//│ static { +//│ Foo1.class = this +//│ } +//│ constructor() {} +//│ static { +//│ let tmp2, tmp3; +//│ tmp2 = Foo1(); +//│ tmp3 = tmp2.foo(); +//│ this.res = tmp3; +//│ } +//│ foo() { +//│ return state +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Foo", []]; +//│ }); + + + +:sjs +type I = Int +//│ JS (unsanitized): +//│ + + + +:sjs +class A(x) with + val y = x +//│ JS (unsanitized): +//│ let A5; +//│ A5 = function A(x1) { +//│ return globalThis.Object.freeze(new A.class(x1)); +//│ }; +//│ (class A4 { +//│ static { +//│ A5.class = this +//│ } +//│ constructor(x1) { +//│ this.#x = x1; +//│ this.y = this.#x; +//│ } +//│ #x; +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A", [null]]; +//│ }); + + + +:sjs +module M with + data class A(x, y) +//│ JS (unsanitized): +//│ let M3; +//│ (class M2 { +//│ static { +//│ M3 = this +//│ } +//│ constructor() { +//│ runtime.Unit; +//│ } +//│ static { +//│ this.A = function A(x1, y1) { +//│ return globalThis.Object.freeze(new A.class(x1, y1)); +//│ }; +//│ (class A6 { +//│ static { +//│ M2.A.class = this +//│ } +//│ constructor(x1, y1) { +//│ this.x = x1; +//│ this.y = y1; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "A", ["x", "y"]]; +//│ }); +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "M"]; +//│ }); + +:sjs +fun f(x) = + while x do + let y = x + x +//│ JS (unsanitized): +//│ let f6; +//│ f6 = function f(x1) { +//│ let tmp2; +//│ tmp3: while (true) { +//│ let y1; +//│ if (x1 === true) { +//│ y1 = x1; +//│ tmp2 = x1; +//│ continue tmp3 +//│ } else { tmp2 = runtime.Unit; } +//│ break; +//│ } +//│ return tmp2 +//│ }; + + +:sjs +:rewriteWhile +fun f(x) = + while x do + let y = x + x +//│ JS (unsanitized): +//│ let f7; +//│ f7 = function f(x1) { +//│ let tmp2, while1, tmp3, tmp4; +//│ tmp2 = undefined; +//│ while1 = (undefined, function () { +//│ let y1; +//│ if (x1 === true) { +//│ y1 = x1; +//│ tmp2 = x1; +//│ return while1() +//│ } else { +//│ tmp2 = runtime.Unit; +//│ } +//│ return runtime.LoopEnd +//│ }); +//│ tmp3 = while1(); +//│ tmp4 = tmp3 !== runtime.LoopEnd; +//│ if (tmp4 === true) { return tmp3 } else { return tmp2 } +//│ }; + + + +data class Bar(x) + +:sjs +data class Baz(z) extends Bar(z * 1) with + val k = 1 + 2 + 3 + fun baz = this.bar * 2 +//│ JS (unsanitized): +//│ let Baz1; +//│ Baz1 = function Baz(z) { +//│ return globalThis.Object.freeze(new Baz.class(z)); +//│ }; +//│ (class Baz extends Bar1.class { +//│ static { +//│ Baz1.class = this +//│ } +//│ constructor(z) { +//│ let tmp2; +//│ tmp2 = z * 1; +//│ super(tmp2); +//│ let tmp3, tmp4; +//│ this.z = z; +//│ tmp3 = 1 + 2; +//│ tmp4 = tmp3 + 3; +//│ this.k = tmp4; +//│ } +//│ get baz() { +//│ return this.bar * 2; +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Baz", ["z"]]; +//│ }); diff --git a/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls new file mode 100644 index 0000000000..fc4f6470d4 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/ScopedBlocksAndHandlers.mls @@ -0,0 +1,100 @@ +:js +:effectHandlers + + + + +fun start(x, f) = + while x do + f(() => x) + +// if `z` and `g` is defined in a nested scoped block +// and `g` is floated out to the top level, `z` in `g` gets unbound. +// so currently nested `if`s do not get nested `Scoped` blocks +:sjs +fun f(x) = + if x do + if x do + let z = 3 + fun g() = z +//│ JS (unsanitized): +//│ let f; +//│ f = function f(x) { +//│ let g, z; +//│ g = function g() { +//│ return z +//│ }; +//│ if (x === true) { +//│ if (x === true) { +//│ z = 3; +//│ return runtime.Unit +//│ } else { return runtime.Unit } +//│ } else { return runtime.Unit } +//│ }; + + + +abstract class Effect with + fun perform() + + + +fun f(x) = + if x then + if x then + let z = 4 + handle h = Effect with + fun perform(x)(k) = + print(x) + k(x + z) + print(h.perform()) + else 0 + + +fun f(x) = + while x do + while x do + let z = 4 + handle h = Effect with + fun perform(x)(k) = + print(x) + k(x + z) + print(h.perform()) + else 0 + + +// The class body generated by the handler is in the +// top level of the function, and it refers to `g`. +// So there would be problems if the declartion of `g` +// is nested inside the first `if`. +:expect 2 +fun foo(h) = + h.perform() + if true then + if true then + fun g() = 2 + g() + else + fun g() = 3 + g() +handle h = Effect with + fun perform()(k) = k(()) +foo(h) +//│ = 2 + + + +fun f(x) = + while x do + while x do + let z = 3 + fun g() = z + g() + + + +fun start(x, f) = + while x do + let z = 2 + fun g() = z + g() diff --git a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls index 0f46270d22..77463ae9a7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SetIn.mls @@ -71,7 +71,8 @@ example() //│ JS (unsanitized): //│ let example2; //│ example2 = function example() { -//│ let x2, get_x, old1, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, get_x1; +//│ let x2, get_x, old1, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11; +//│ let get_x1; //│ x2 = 0; //│ get_x1 = function get_x() { //│ return x2 @@ -112,7 +113,8 @@ example() //│ JS (unsanitized): //│ let example3; //│ example3 = function example() { -//│ let x2, get_x, y, old1, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, get_x1; +//│ let x2, get_x, y, old1, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; +//│ let get_x1; //│ x2 = 0; //│ get_x1 = function get_x() { //│ return x2 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls index 4636263464..74008c12e8 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 f2; f2 = function f(x) { throw globalThis.Error("e") }; f2(1) +//│ let f2; f2 = function f(x) { let y; throw globalThis.Error("e") }; f2(1) //│ ═══[RUNTIME ERROR] Error: e diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index 76e87c7a78..df39812ba5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -6,22 +6,19 @@ //│ JS (unsanitized): //│ let lambda; //│ lambda = (undefined, function () { -//│ let while1, tmp, tmp1, tmp2; -//│ tmp = undefined; -//│ while1 = (undefined, function () { +//│ let tmp; +//│ tmp1: while (true) { //│ let scrut; //│ scrut = true; //│ if (scrut === true) { //│ tmp = 0; -//│ return while1() +//│ continue tmp1 //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } -//│ /* unreachable */ -//│ }); -//│ tmp1 = while1(); -//│ tmp2 = tmp1 !== runtime.LoopEnd; -//│ if (tmp2 === true) { return tmp1 } else { return tmp } +//│ break; +//│ } +//│ return tmp //│ }); //│ lambda //│ = fun @@ -57,22 +54,17 @@ while x set x = false else 42 //│ JS (unsanitized): -//│ let while3, tmp4, tmp5; -//│ tmp4 = undefined; -//│ while3 = (undefined, function () { +//│ let tmp4; +//│ tmp5: while (true) { //│ let tmp6; -//│ loopLabel: while (true) { -//│ if (x2 === true) { -//│ tmp6 = Predef.print("Hello World"); -//│ x2 = false; -//│ tmp4 = runtime.Unit; -//│ continue loopLabel -//│ } else { tmp4 = 42; } -//│ return runtime.LoopEnd; -//│ break; -//│ } -//│ }); -//│ tmp5 = while3(); +//│ if (x2 === true) { +//│ tmp6 = Predef.print("Hello World"); +//│ x2 = false; +//│ tmp4 = runtime.Unit; +//│ continue tmp5 +//│ } else { tmp4 = 42; } +//│ break; +//│ } //│ tmp4 //│ > Hello World //│ = 42 @@ -115,25 +107,20 @@ while //│ JS (unsanitized): //│ let lambda2; //│ lambda2 = (undefined, function () { -//│ let while7, tmp12, tmp13, tmp14; -//│ tmp12 = undefined; -//│ while7 = (undefined, function () { -//│ let i2, scrut, tmp15; +//│ let tmp19; +//│ tmp20: while (true) { +//│ let i2, scrut3, tmp21; //│ i2 = 0; -//│ scrut = i2 < 10; -//│ if (scrut === true) { -//│ tmp15 = i2 + 1; -//│ i2 = tmp15; -//│ tmp12 = runtime.Unit; -//│ return while7() -//│ } else { -//│ tmp12 = runtime.Unit; -//│ } -//│ return runtime.LoopEnd -//│ }); -//│ tmp13 = while7(); -//│ tmp14 = tmp13 !== runtime.LoopEnd; -//│ if (tmp14 === true) { return tmp13 } else { return tmp12 } +//│ scrut3 = i2 < 10; +//│ if (scrut3 === true) { +//│ tmp21 = i2 + 1; +//│ i2 = tmp21; +//│ tmp19 = runtime.Unit; +//│ continue tmp20 +//│ } else { tmp19 = runtime.Unit; } +//│ break; +//│ } +//│ return tmp19 //│ }); //│ lambda2 //│ = fun @@ -213,9 +200,8 @@ fun f(ls) = //│ JS (unsanitized): //│ let f; //│ f = function f(ls) { -//│ let while10, tmp18, tmp19, tmp20; -//│ tmp18 = undefined; -//│ while10 = (undefined, function () { +//│ let tmp28; +//│ tmp29: while (true) { //│ let tl, h, argument0$, argument1$; //│ if (ls instanceof Cons1.class) { //│ argument0$ = ls.hd; @@ -223,16 +209,12 @@ fun f(ls) = //│ tl = argument1$; //│ h = argument0$; //│ ls = tl; -//│ tmp18 = Predef.print(h); -//│ return while10() -//│ } else { -//│ tmp18 = Predef.print("Done!"); -//│ } -//│ return runtime.LoopEnd -//│ }); -//│ tmp19 = while10(); -//│ tmp20 = tmp19 !== runtime.LoopEnd; -//│ if (tmp20 === true) { return tmp19 } else { return tmp18 } +//│ tmp28 = Predef.print(h); +//│ continue tmp29 +//│ } else { tmp28 = Predef.print("Done!"); } +//│ break; +//│ } +//│ return tmp28 //│ }; f(0) @@ -270,23 +252,19 @@ let x = 1 :sjs while x is {} do() //│ JS (unsanitized): -//│ let while11, tmp27, tmp28; -//│ tmp27 = undefined; -//│ while11 = (undefined, function () { +//│ let tmp37; +//│ tmp38: while (true) { //│ split_root$: { //│ split_1$: { //│ if (x3 instanceof Object) { //│ break split_1$ -//│ } else { -//│ break split_1$ -//│ } +//│ } else { break split_1$ } //│ } -//│ tmp27 = runtime.Unit; +//│ tmp37 = runtime.Unit; //│ } -//│ return runtime.LoopEnd -//│ }); -//│ tmp28 = while11(); -//│ tmp27 +//│ break; +//│ } +//│ tmp37 // ——— FIXME: ——— @@ -300,10 +278,10 @@ while print("Hello World"); false then 0(0) else 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.300: then 0(0) +//│ ║ l.278: then 0(0) //│ ╙── ^^^^ //│ ╔══[ERROR] Unrecognized term split (false literal) -//│ ║ l.299: while print("Hello World"); false +//│ ║ l.277: while print("Hello World"); false //│ ╙── ^^^^^ //│ > Hello World //│ ═══[RUNTIME ERROR] Error: match error @@ -313,12 +291,12 @@ while { print("Hello World"), false } then 0(0) else 1 //│ ╔══[ERROR] Unexpected infix use of keyword 'then' here -//│ ║ l.312: while { print("Hello World"), false } +//│ ║ l.290: while { print("Hello World"), false } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.313: then 0(0) +//│ ║ l.291: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[ERROR] Illegal position for prefix keyword 'else'. -//│ ║ l.314: else 1 +//│ ║ l.292: else 1 //│ ╙── ^^^^ :fixme @@ -328,14 +306,14 @@ while then 0(0) else 1 //│ ╔══[ERROR] Unexpected infix use of keyword 'then' here -//│ ║ l.326: print("Hello World") +//│ ║ l.304: print("Hello World") //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.327: false +//│ ║ l.305: false //│ ║ ^^^^^^^^^ -//│ ║ l.328: then 0(0) +//│ ║ l.306: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[ERROR] Illegal position for prefix keyword 'else'. -//│ ║ l.329: else 1 +//│ ║ l.307: else 1 //│ ╙── ^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls b/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls index 32ddcfe6a9..50a8c0745d 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls @@ -31,7 +31,8 @@ using Num = 0.42 :sjs let f1 = foo //│ JS (unsanitized): -//│ let f1, f11; +//│ let f1; +//│ let f11; //│ f11 = function f1() { //│ let tmp; //│ tmp = foo(); @@ -52,7 +53,8 @@ f1() :sjs let f2 = bar //│ JS (unsanitized): -//│ let f2, f21; +//│ let f2; +//│ let f21; //│ f21 = function f2(x) { //│ let tmp; //│ tmp = bar(x); @@ -73,7 +75,8 @@ f2(0.42) :sjs let f3 = baz //│ JS (unsanitized): -//│ let f3, f31; +//│ let f3; +//│ let f31; //│ f31 = function f3(x) { //│ let lambda1; //│ lambda1 = (undefined, function (y) { diff --git a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls index 2572b03c45..9ac3e9192e 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions2.mls @@ -16,66 +16,38 @@ x val x -:ge +// with Scoped blocks, `x` is declared +// :ge x -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' -//│ ║ l.20: x -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.17: val x -//│ ╙── ^^^^^ val x: Int -:ge +// with Scoped blocks, `x` is declared +// :ge x -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' -//│ ║ l.32: x -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.29: val x: Int -//│ ╙── ^^^^^^^^^^ fun p: Int -:ge +:re p -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'p' -//│ ║ l.44: p -//│ ║ ^ -//│ ╟── which references the symbol introduced here -//│ ║ l.41: fun p: Int -//│ ╙── ^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: p is not defined +//│ ═══[RUNTIME ERROR] TypeError: p is not a function // :ctx fun (++) test: (Int, Int) -> Int -:ge +:re test(1, 2) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'test' -//│ ║ l.58: test(1, 2) -//│ ║ ^^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.55: fun (++) test: (Int, Int) -> Int -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: test is not defined +//│ ═══[RUNTIME ERROR] TypeError: test is not a function :sjs -:ge +:re 1 ++ 1 -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'test' -//│ ║ l.69: 1 ++ 1 -//│ ║ ^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.55: fun (++) test: (Int, Int) -> Int -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^ //│ JS (unsanitized): //│ let tmp2; tmp2 = test(); tmp2(1, 1) -//│ ═══[RUNTIME ERROR] ReferenceError: test is not defined +//│ ═══[RUNTIME ERROR] TypeError: test is not a function declare class C diff --git a/hkmc2/shared/src/test/mlscript/ctx/TrickyResolution.mls b/hkmc2/shared/src/test/mlscript/ctx/TrickyResolution.mls index 0236beeda4..d9a8abb01a 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/TrickyResolution.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/TrickyResolution.mls @@ -21,12 +21,6 @@ using Bar[Str] = new Bar // * and thus picks the Bar[Int] instance :fixme foo -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'foo' -//│ ║ l.23: foo -//│ ║ ^^^ -//│ ╟── which references the symbol introduced here -//│ ║ l.11: fun foo[A](using f: Foo[A], b: Bar[A]): A -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: foo is not defined +//│ ═══[RUNTIME ERROR] TypeError: foo1 is not a function diff --git a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls index 7dfd0af73f..10f857e48e 100644 --- a/hkmc2/shared/src/test/mlscript/decls/Prelude.mls +++ b/hkmc2/shared/src/test/mlscript/decls/Prelude.mls @@ -208,6 +208,9 @@ declare module annotations with object buffered object bufferable +declare module scope with + fun locally + // HTML DOM API definitions. // Move them to a separate Prelude file when there are enough of them. diff --git a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls index d880400f9d..bd38be2a12 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Debugging.mls @@ -17,7 +17,8 @@ fun f() = print of raiseUnhandledEffect() j / i //│ JS (unsanitized): -//│ let f, getLocals2; +//│ let f; +//│ let getLocals2; //│ getLocals2 = function getLocals() { //│ let prev, thisInfo, arr, tmp; //│ prev = []; @@ -27,7 +28,8 @@ fun f() = //│ return prev //│ }; //│ f = function f() { -//│ let i, j, k, scrut, tmp, tmp1, getLocals3, Cont$func$f$1, doUnwind; +//│ let i, j, k, scrut, tmp, tmp1; +//│ let getLocals3, Cont$func$f$1, doUnwind; //│ getLocals3 = function getLocals() { //│ let i1, j1, k1, prev, thisInfo, arr, tmp2; //│ prev = getLocals2(); @@ -161,8 +163,8 @@ lambda_test(() => raiseUnhandledEffect() 100) //│ ═══[RUNTIME ERROR] Error: Unhandled effect FatalEffect -//│ at lambda (Debugging.mls:161:3) -//│ at lambda_test (Debugging.mls:158:3) +//│ at lambda (Debugging.mls:163:3) +//│ at lambda_test (Debugging.mls:160:3) import "../../mlscript-compile/Runtime.mls" @@ -241,9 +243,9 @@ fun f() = f() //│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 200)])] //│ > Stack Trace: -//│ > at f (Debugging.mls:234:3) with locals: j=200 +//│ > at f (Debugging.mls:236:3) with locals: j=200 //│ > Stack Trace: -//│ > at f (Debugging.mls:236:3) +//│ > at f (Debugging.mls:238:3) //│ > Stack Trace: -//│ > at f (Debugging.mls:237:3) with locals: j=300 +//│ > at f (Debugging.mls:239:3) with locals: j=300 //│ > [FnLocalsInfo("‹top level›", [LocalVarInfo("i", 100)]), FnLocalsInfo("f", [LocalVarInfo("j", 300)])] diff --git a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls index e4d06c5720..bee0bd40eb 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/Effects.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/Effects.mls @@ -156,16 +156,17 @@ if true do fun f() = 3 f() //│ JS (unsanitized): -//│ let tmp11, handleBlock$11; +//│ let f, scrut, tmp20; +//│ let handleBlock$11; //│ handleBlock$11 = function handleBlock$() { -//│ let f, h, scrut, res, Cont$handleBlock$h$11, doUnwind, Handler$h$12; +//│ let h, res, Cont$handleBlock$h$11, doUnwind, Handler$h$12; //│ (class Handler$h$11 extends Effect1 { //│ static { //│ Handler$h$12 = this //│ } //│ constructor() { -//│ let tmp12; -//│ tmp12 = super(); +//│ let tmp21; +//│ tmp21 = super(); //│ } //│ perform(arg) { //│ let hdlrFun; @@ -183,8 +184,8 @@ if true do //│ Cont$handleBlock$h$11 = this //│ } //│ constructor(pc) { -//│ let tmp12; -//│ tmp12 = super(null); +//│ let tmp21; +//│ tmp21 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { @@ -219,11 +220,11 @@ if true do //│ return runtime.Unit //│ } //│ }; -//│ tmp11 = handleBlock$11(); -//│ if (tmp11 instanceof runtime.EffectSig.class) { -//│ tmp11 = runtime.topLevelEffect(tmp11, false); +//│ tmp20 = handleBlock$11(); +//│ if (tmp20 instanceof runtime.EffectSig.class) { +//│ tmp20 = runtime.topLevelEffect(tmp20, false); //│ } -//│ tmp11 +//│ tmp20 //│ = 3 module A with diff --git a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls index a4e3cefa6f..714e88b636 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/EffectsInClasses.mls @@ -19,7 +19,8 @@ data class Lol(h) with //│ Lol1.class = this //│ } //│ constructor(h) { -//│ let tmp, res, Cont$ctor$Lol$1, doUnwind; +//│ let tmp; +//│ let res, Cont$ctor$Lol$1, doUnwind; //│ const this$Lol = this; //│ (class Cont$ctor$Lol$ extends runtime.FunctionContFrame.class { //│ static { diff --git a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls index 160455349a..9d46462f07 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/RecursiveHandlers.mls @@ -155,41 +155,43 @@ if true do h1.perform(()) str //│ JS (unsanitized): -//│ let str, scrut, tmp9, tmp10, handleBlock$7; +//│ let str, scrut, tmp24, tmp25, tmp26, tmp27; +//│ let handleBlock$7; //│ str = ""; //│ scrut = true; //│ if (scrut === true) { //│ handleBlock$7 = function handleBlock$() { -//│ let h1, tmp11, handleBlock$8, Cont$handleBlock$h1$2, doUnwind, Handler$h1$2; +//│ let h1, handleBlock$8, Cont$handleBlock$h1$2, doUnwind, Handler$h1$2; //│ (class Handler$h1$1 extends Effect1 { //│ static { //│ Handler$h1$2 = this //│ } //│ constructor() { -//│ let tmp12; -//│ tmp12 = super(); +//│ let tmp28; +//│ tmp28 = super(); //│ } //│ perform(arg) { //│ let hdlrFun; //│ hdlrFun = function hdlrFun(k) { -//│ let tmp12, tmp13, tmp14, Cont$handler$h1$perform$2, doUnwind1; +//│ let tmp28, tmp29, tmp30; +//│ let Cont$handler$h1$perform$2, doUnwind1; //│ (class Cont$handler$h1$perform$1 extends runtime.FunctionContFrame.class { //│ static { //│ Cont$handler$h1$perform$2 = this //│ } //│ constructor(pc) { -//│ let tmp15; -//│ tmp15 = super(null); +//│ let tmp31; +//│ tmp31 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 6) { -//│ tmp13 = value$; +//│ tmp29 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 6) { -//│ tmp14 = str + "A"; -//│ str = tmp14; +//│ tmp30 = str + "A"; +//│ str = tmp30; //│ return runtime.Unit //│ } //│ break; @@ -203,14 +205,14 @@ str //│ res7.contTrace.last = res7.contTrace.last.next; //│ return res7 //│ }; -//│ tmp12 = str + "A"; -//│ str = tmp12; -//│ tmp13 = runtime.safeCall(k(arg)); -//│ if (tmp13 instanceof runtime.EffectSig.class) { -//│ return doUnwind1(tmp13, 6) +//│ tmp28 = str + "A"; +//│ str = tmp28; +//│ tmp29 = runtime.safeCall(k(arg)); +//│ if (tmp29 instanceof runtime.EffectSig.class) { +//│ return doUnwind1(tmp29, 6) //│ } -//│ tmp14 = str + "A"; -//│ str = tmp14; +//│ tmp30 = str + "A"; +//│ str = tmp30; //│ return runtime.Unit //│ }; //│ return runtime.mkEffect(this, hdlrFun) @@ -224,17 +226,17 @@ str //│ Cont$handleBlock$h1$2 = this //│ } //│ constructor(pc) { -//│ let tmp12; -//│ tmp12 = super(null); +//│ let tmp28; +//│ tmp28 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 5) { -//│ tmp11 = value$; +//│ tmp25 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 5) { -//│ return tmp11 +//│ return tmp25 //│ } //│ break; //│ } @@ -247,37 +249,38 @@ str //│ return runtime.handleBlockImpl(res7, h1) //│ }; //│ handleBlock$8 = function handleBlock$() { -//│ let h2, tmp12, res7, Cont$handleBlock$h2$2, doUnwind1, Handler$h2$2; +//│ let h2, res7, Cont$handleBlock$h2$2, doUnwind1, Handler$h2$2; //│ (class Handler$h2$1 extends Effect1 { //│ static { //│ Handler$h2$2 = this //│ } //│ constructor() { -//│ let tmp13; -//│ tmp13 = super(); +//│ let tmp28; +//│ tmp28 = super(); //│ } //│ perform(arg) { //│ let hdlrFun; //│ hdlrFun = function hdlrFun(k) { -//│ let tmp13, tmp14, tmp15, tmp16, tmp17, Cont$handler$h2$perform$2, doUnwind2; +//│ let tmp28, tmp29, tmp30, tmp31, tmp32; +//│ let Cont$handler$h2$perform$2, doUnwind2; //│ (class Cont$handler$h2$perform$1 extends runtime.FunctionContFrame.class { //│ static { //│ Cont$handler$h2$perform$2 = this //│ } //│ constructor(pc) { -//│ let tmp18; -//│ tmp18 = super(null); +//│ let tmp33; +//│ tmp33 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ if (this.pc === 3) { -//│ tmp15 = value$; +//│ tmp30 = value$; //│ } //│ contLoop: while (true) { //│ if (this.pc === 3) { -//│ tmp16 = str + "B"; -//│ tmp17 = str + tmp16; -//│ str = tmp17; +//│ tmp31 = str + "B"; +//│ tmp32 = str + tmp31; +//│ str = tmp32; //│ return runtime.Unit //│ } //│ break; @@ -291,16 +294,16 @@ str //│ res8.contTrace.last = res8.contTrace.last.next; //│ return res8 //│ }; -//│ tmp13 = str + "B"; -//│ tmp14 = str + tmp13; -//│ str = tmp14; -//│ tmp15 = runtime.safeCall(k(arg)); -//│ if (tmp15 instanceof runtime.EffectSig.class) { -//│ return doUnwind2(tmp15, 3) +//│ tmp28 = str + "B"; +//│ tmp29 = str + tmp28; +//│ str = tmp29; +//│ tmp30 = runtime.safeCall(k(arg)); +//│ if (tmp30 instanceof runtime.EffectSig.class) { +//│ return doUnwind2(tmp30, 3) //│ } -//│ tmp16 = str + "B"; -//│ tmp17 = str + tmp16; -//│ str = tmp17; +//│ tmp31 = str + "B"; +//│ tmp32 = str + tmp31; +//│ str = tmp32; //│ return runtime.Unit //│ }; //│ return runtime.mkEffect(this, hdlrFun) @@ -314,14 +317,14 @@ str //│ Cont$handleBlock$h2$2 = this //│ } //│ constructor(pc) { -//│ let tmp13; -//│ tmp13 = super(null); +//│ let tmp28; +//│ tmp28 = super(null); //│ this.pc = pc; //│ } //│ resume(value$) { //│ switch (this.pc) { //│ case 1: -//│ tmp12 = value$; +//│ tmp26 = value$; //│ break; //│ case 2: //│ res7 = value$; @@ -351,9 +354,9 @@ str //│ res8.contTrace.last.next = new Cont$handleBlock$h2$2(pc); //│ return runtime.handleBlockImpl(res8, h2) //│ }; -//│ tmp12 = runtime.safeCall(h2.perform(runtime.Unit)); -//│ if (tmp12 instanceof runtime.EffectSig.class) { -//│ return doUnwind1(tmp12, 1) +//│ tmp26 = runtime.safeCall(h2.perform(runtime.Unit)); +//│ if (tmp26 instanceof runtime.EffectSig.class) { +//│ return doUnwind1(tmp26, 1) //│ } //│ res7 = runtime.safeCall(h1.perform(runtime.Unit)); //│ if (res7 instanceof runtime.EffectSig.class) { @@ -361,18 +364,18 @@ str //│ } //│ return res7 //│ }; -//│ tmp11 = handleBlock$8(); -//│ if (tmp11 instanceof runtime.EffectSig.class) { -//│ return doUnwind(tmp11, 5) +//│ tmp25 = handleBlock$8(); +//│ if (tmp25 instanceof runtime.EffectSig.class) { +//│ return doUnwind(tmp25, 5) //│ } -//│ return tmp11 +//│ return tmp25 //│ }; -//│ tmp9 = handleBlock$7(); -//│ if (tmp9 instanceof runtime.EffectSig.class) { -//│ tmp9 = runtime.topLevelEffect(tmp9, false); +//│ tmp24 = handleBlock$7(); +//│ if (tmp24 instanceof runtime.EffectSig.class) { +//│ tmp24 = runtime.topLevelEffect(tmp24, false); //│ } -//│ tmp10 = tmp9; -//│ } else { tmp10 = runtime.Unit; } +//│ tmp27 = tmp24; +//│ } else { tmp27 = runtime.Unit; } //│ str //│ = "BABABA" //│ str = "BABABA" diff --git a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls index 891bf1cf8c..e95579615e 100644 --- a/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls +++ b/hkmc2/shared/src/test/mlscript/handlers/StackSafety.mls @@ -23,9 +23,11 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, res, $_stack$_safe$_body$_; +//│ let hi; +//│ let res, $_stack$_safe$_body$_; //│ hi = function hi(n) { -//│ let scrut, tmp, Cont$func$hi$1, doUnwind, stackDelayRes; +//│ let scrut, tmp; +//│ let Cont$func$hi$1, doUnwind, stackDelayRes; //│ (class Cont$func$hi$ extends runtime.FunctionContFrame.class { //│ static { //│ Cont$func$hi$1 = this @@ -77,9 +79,11 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, res1, $_stack$_safe$_body$_1; +//│ let sum1; +//│ let res1, $_stack$_safe$_body$_1; //│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, Cont$func$sum$1, doUnwind, curDepth, stackDelayRes; +//│ let scrut, tmp, tmp1; +//│ let Cont$func$sum$1, doUnwind, curDepth, stackDelayRes; //│ (class Cont$func$sum$ extends runtime.FunctionContFrame.class { //│ static { //│ Cont$func$sum$1 = this diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 55e9ea2013..24f10b87ad 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -88,16 +88,17 @@ fun f() = Good() f().foo() //│ JS (unsanitized): -//│ let Bad1, Good1, f5, tmp5, Bad$, Good$, f$capture3; +//│ let f6, tmp9; +//│ let Bad1, Good1, Bad$, Good$, f$capture3; //│ Good$ = function Good$(isMut, x, y, z, f$capture4) { -//│ let tmp6, tmp7; +//│ let tmp10, tmp11; //│ if (isMut === true) { -//│ tmp6 = new Good1.class(); +//│ tmp10 = new Good1.class(); //│ } else { -//│ tmp6 = globalThis.Object.freeze(new Good1.class()); +//│ tmp10 = globalThis.Object.freeze(new Good1.class()); //│ } -//│ tmp7 = tmp6(x, y, z, f$capture4); -//│ return tmp7 +//│ tmp11 = tmp10(x, y, z, f$capture4); +//│ return tmp11 //│ }; //│ Good1 = function Good() { //│ return (x, y, z, f$capture4) => { @@ -130,24 +131,24 @@ f().foo() //│ get z() { return this.#z; } //│ set z(value) { this.#z = value; } //│ foo() { -//│ let tmp6, tmp7; +//│ let tmp10, tmp11; //│ this.z = 100; -//│ tmp6 = this.x + this.y; -//│ tmp7 = tmp6 + this.z; -//│ return tmp7 + this.f$capture.w$capture$0 +//│ tmp10 = this.x + this.y; +//│ tmp11 = tmp10 + this.z; +//│ return tmp11 + this.f$capture.w$capture$0 //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Good", []]; //│ }); //│ Bad$ = function Bad$(isMut, f$capture4) { -//│ let tmp6, tmp7; +//│ let tmp10, tmp11; //│ if (isMut === true) { -//│ tmp6 = new Bad1.class(); +//│ tmp10 = new Bad1.class(); //│ } else { -//│ tmp6 = globalThis.Object.freeze(new Bad1.class()); +//│ tmp10 = globalThis.Object.freeze(new Bad1.class()); //│ } -//│ tmp7 = tmp6(f$capture4); -//│ return tmp7 +//│ tmp11 = tmp10(f$capture4); +//│ return tmp11 //│ }; //│ Bad1 = function Bad() { //│ return (f$capture4) => { @@ -184,19 +185,20 @@ f().foo() //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "f$capture"]; //│ }); -//│ f5 = function f() { -//│ let x, y, z, tmp6, tmp7, capture; +//│ f6 = function f() { +//│ let x, y, z, w, tmp10, tmp11; +//│ let capture; //│ capture = new f$capture3(null); //│ x = 1; //│ y = 10; //│ z = 10; //│ capture.w$capture$0 = 1000; -//│ tmp6 = Bad$(false, capture); -//│ tmp7 = tmp6.foo(); +//│ tmp10 = Bad$(false, capture); +//│ tmp11 = tmp10.foo(); //│ return Good$(false, x, y, z, capture) //│ }; -//│ tmp5 = f5(); -//│ runtime.safeCall(tmp5.foo()) +//│ tmp9 = f6(); +//│ runtime.safeCall(tmp9.foo()) //│ = 10111 :expect 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls index cf57dc78d0..5b9ef2ff7f 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/CompanionsInFun.mls @@ -11,12 +11,13 @@ fun f(x) = fun g = new A //│ ═══[WARNING] Modules are not yet lifted. //│ JS (unsanitized): -//│ let f, g$; +//│ let f; +//│ let g$; //│ g$ = function g$(A$member, A1, x) { //│ return globalThis.Object.freeze(new A$member()) //│ }; //│ f = function f(x) { -//│ let A1; +//│ let A1, g; //│ (class A { //│ static { //│ A1 = this diff --git a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls index 8adf8005d3..0703c32952 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/DefnsInClass.mls @@ -7,7 +7,8 @@ data class A(x) with fun getB() = x + y fun getA() = B(2).getB() //│ JS (unsanitized): -//│ let B1, A1, B$; +//│ let A1; +//│ let B1, B$; //│ B$ = function B$(isMut, A$instance, y) { //│ let tmp, tmp1; //│ if (isMut === true) { @@ -73,7 +74,8 @@ class A with g (new A).x() //│ JS (unsanitized): -//│ let g, A3, tmp1, g$; +//│ let A3, tmp1; +//│ let g, g$; //│ g$ = function g$(A$instance) { //│ return 2 //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls index 4c10141555..f8cdbc0ccb 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/FunInFun.mls @@ -1,6 +1,28 @@ :lift :js + +:fixme +fun foo = + let x + fun bar = x +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' +//│ ║ l.8: fun bar = x +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.7: let x +//│ ╙── ^ + +:expect 12 +fun foo = + let x + fun bar = x + x = 12 + bar +foo +//│ = 12 + + :expect 5 fun f(used1, unused1) = let used2 = unused1 @@ -47,7 +69,8 @@ fun f(used1, unused1) = foo(used2) + unused2 f(1, 2) //│ JS (unsanitized): -//│ let g1, f3, g$3; +//│ let f3; +//│ let g1, g$3; //│ g$3 = function g$(used1, used2, g_arg) { //│ let used3; //│ used3 = 2; @@ -59,13 +82,14 @@ f(1, 2) //│ } //│ }; //│ f3 = function f(used1, unused1) { -//│ let used2, unused2, foo, tmp, g$here; +//│ let used2, unused2, foo2, tmp1; +//│ let g$here; //│ used2 = unused1; //│ unused2 = 2; //│ g$here = runtime.safeCall(g1(used1, used2)); -//│ foo = g$here; -//│ tmp = runtime.safeCall(foo(used2)); -//│ return tmp + unused2 +//│ foo2 = g$here; +//│ tmp1 = runtime.safeCall(foo2(used2)); +//│ return tmp1 + unused2 //│ }; //│ f3(1, 2) //│ = 5 @@ -77,19 +101,20 @@ fun f(a1, a2, a3, a4, a5, a6) = g f(1,2,3,4,5,6) //│ JS (unsanitized): -//│ let f4, g$4; +//│ let f4; +//│ let g$4; //│ g$4 = 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 +//│ let tmp1, tmp2, tmp3, tmp4; +//│ tmp1 = a1 + a2; +//│ tmp2 = tmp1 + a3; +//│ tmp3 = tmp2 + a4; +//│ tmp4 = tmp3 + a5; +//│ return tmp4 + a6 //│ }; //│ f4 = function f(a1, a2, a3, a4, a5, a6) { -//│ let tmp; -//│ tmp = g$4(a1, a2, a3, a4, a5, a6); -//│ return tmp +//│ let g2, tmp1; +//│ tmp1 = g$4(a1, a2, a3, a4, a5, a6); +//│ return tmp1 //│ }; //│ f4(1, 2, 3, 4, 5, 6) //│ = 21 @@ -162,7 +187,8 @@ fun f(unused, immutable, mutated) = a + h() + unused f(1, 2, 1000) //│ JS (unsanitized): -//│ let f7, h$2, g$6, f$capture5; +//│ let f7; +//│ let h$2, g$6, f$capture5; //│ g$6 = function g$(immutable, f$capture6) { //│ f$capture6.mutated$capture$0 = 2; //│ return immutable + f$capture6.mutated$capture$0 @@ -181,12 +207,13 @@ f(1, 2, 1000) //│ static [definitionMetadata] = ["class", "f$capture"]; //│ }); //│ f7 = function f(unused, immutable, mutated) { -//│ let a1, tmp7, tmp8, capture; +//│ let g3, h2, a1, tmp8, tmp9; +//│ let capture; //│ capture = new f$capture5(mutated); //│ a1 = g$6(immutable, capture); -//│ tmp7 = h$2(capture); -//│ tmp8 = a1 + tmp7; -//│ return tmp8 + unused +//│ tmp8 = h$2(capture); +//│ tmp9 = a1 + tmp8; +//│ return tmp9 + unused //│ }; //│ f7(1, 2, 1000) //│ = 7 @@ -251,14 +278,15 @@ fun g() = f g()(1) //│ JS (unsanitized): -//│ let f14, g6, tmp7, f$1, h$4, g$capture1; +//│ let g6, tmp8; +//│ let f14, f$1, h$4, g$capture1; //│ h$4 = function h$(x1, k, g$capture2) { //│ k = 5; //│ x1 = 4; //│ return x1 + g$capture2.y$capture$0 //│ }; //│ f$1 = function f$(g$capture2, x1) { -//│ let k; +//│ let h3, k; //│ k = 4; //│ g$capture2.y$capture$0 = 2; //│ return x1 @@ -279,14 +307,15 @@ g()(1) //│ static [definitionMetadata] = ["class", "g$capture"]; //│ }); //│ g6 = function g() { +//│ let y1; //│ let capture, f$here; //│ capture = new g$capture1(null); //│ capture.y$capture$0 = 0; //│ f$here = runtime.safeCall(f14(capture)); //│ return f$here //│ }; -//│ tmp7 = g6(); -//│ runtime.safeCall(tmp7(1)) +//│ tmp8 = g6(); +//│ runtime.safeCall(tmp8(1)) //│ = 1 :expect 2 @@ -370,7 +399,8 @@ fun f(x) = set y = 2 [g, g] //│ JS (unsanitized): -//│ let g12, f23, g$14; +//│ let f23; +//│ let g12, g$14; //│ g$14 = function g$(y1) { //│ return y1 //│ }; @@ -380,8 +410,8 @@ fun f(x) = //│ } //│ }; //│ f23 = function f(x1) { -//│ let y1, scrut, g$here; -//│ y1 = undefined; +//│ let y1, scrut; +//│ let g$here; //│ scrut = x1 < 0; //│ if (scrut === true) { //│ y1 = 1; @@ -404,7 +434,8 @@ fun f(x) = set x += 1 [a, g] //│ JS (unsanitized): -//│ let g13, f24, g$15, f$capture17; +//│ let f24; +//│ let g13, g$15, f$capture17; //│ g$15 = function g$(f$capture18) { //│ return f$capture18.x$capture$0 //│ }; @@ -424,12 +455,13 @@ fun f(x) = //│ static [definitionMetadata] = ["class", "f$capture"]; //│ }); //│ f24 = function f(x1) { -//│ let a1, tmp10, capture, g$here; +//│ let a1, tmp11; +//│ let capture, g$here; //│ capture = new f$capture17(x1); //│ g$here = runtime.safeCall(g13(capture)); //│ a1 = g$here; -//│ tmp10 = capture.x$capture$0 + 1; -//│ capture.x$capture$0 = tmp10; +//│ tmp11 = capture.x$capture$0 + 1; +//│ capture.x$capture$0 = tmp11; //│ g$here = runtime.safeCall(g13(capture)); //│ return globalThis.Object.freeze([ a1, g$here ]) //│ }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls index e2713dcc59..dcf2ad4d80 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Loops.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Loops.mls @@ -20,7 +20,6 @@ fs.0() let fs = mut [] //│ fs = [] -:dontRewriteWhile fun foo() = let i = 1 while i < 5 do @@ -45,47 +44,32 @@ fun foo() = set x += 1 return () => x //│ JS (unsanitized): -//│ let foo2, lambda2, while$1, lambda$2, foo$capture5; -//│ lambda$2 = function lambda$(foo$capture6) { -//│ return foo$capture6.x$capture$0 +//│ let foo2; +//│ let lambda2, lambda$2; +//│ lambda$2 = function lambda$(x) { +//│ return x //│ }; -//│ lambda2 = (undefined, function (foo$capture6) { +//│ lambda2 = (undefined, function (x) { //│ return () => { -//│ return lambda$2(foo$capture6) +//│ return lambda$2(x) //│ } //│ }); -//│ while$1 = function while$(foo$capture6) { -//│ let scrut, tmp2, lambda$here; -//│ scrut = true; -//│ if (scrut === true) { -//│ tmp2 = foo$capture6.x$capture$0 + 1; -//│ foo$capture6.x$capture$0 = tmp2; -//│ lambda$here = runtime.safeCall(lambda2(foo$capture6)); -//│ return lambda$here -//│ } else { -//│ foo$capture6.tmp$capture$1 = runtime.Unit; -//│ } -//│ return runtime.LoopEnd -//│ }; -//│ (class foo$capture4 { -//│ static { -//│ foo$capture5 = this -//│ } -//│ constructor(x$capture$0, tmp$capture$1) { -//│ this.tmp$capture$1 = tmp$capture$1; -//│ this.x$capture$0 = x$capture$0; -//│ } -//│ toString() { return runtime.render(this); } -//│ static [definitionMetadata] = ["class", "foo$capture"]; -//│ }); //│ foo2 = function foo() { -//│ let tmp2, tmp3, capture; -//│ capture = new foo$capture5(null, null); -//│ capture.x$capture$0 = 1; -//│ capture.tmp$capture$1 = undefined; -//│ tmp2 = while$1(capture); -//│ tmp3 = tmp2 !== runtime.LoopEnd; -//│ if (tmp3 === true) { return tmp2 } else { return capture.tmp$capture$1 } +//│ let x, tmp2; +//│ let lambda$here; +//│ x = 1; +//│ tmp3: while (true) { +//│ let scrut, tmp4; +//│ scrut = true; +//│ if (scrut === true) { +//│ tmp4 = x + 1; +//│ x = tmp4; +//│ lambda$here = runtime.safeCall(lambda2(x)); +//│ return lambda$here +//│ } else { tmp2 = runtime.Unit; } +//│ break; +//│ } +//│ return tmp2 //│ }; :expect 2 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls index e0db79b6c1..8b09a22289 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ModulesObjects.mls @@ -27,7 +27,8 @@ fun foo(y) = (new M).foo() foo(10) //│ JS (unsanitized): -//│ let M3, foo1, M$; +//│ let foo1; +//│ let M3, M$; //│ M$ = function M$(isMut, y) { //│ let tmp; //│ if (isMut === true) { @@ -109,7 +110,8 @@ fun foo(x, y) = fun foo3 = M.foo2() foo3 //│ JS (unsanitized): -//│ let M5, foo4, foo3$, foo$capture3; +//│ let foo4; +//│ let M5, foo3$, foo$capture3; //│ foo3$ = function foo3$(M6, x, foo$capture4) { //│ return M6.foo2() //│ }; @@ -145,7 +147,8 @@ fun foo(x, y) = //│ static [definitionMetadata] = ["class", "foo$capture"]; //│ }); //│ foo4 = function foo(x, y) { -//│ let tmp, M$1, capture; +//│ let foo31, tmp; +//│ let M$1, capture; //│ capture = new foo$capture3(y); //│ M$1 = globalThis.Object.freeze(new M5(x, capture)); //│ tmp = foo3$(M$1, x, capture); diff --git a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls index afd4ad49a8..bf21ce9b2e 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/Mutation.mls @@ -12,7 +12,8 @@ fun foo() = xs.push(bar) set x = 2 //│ JS (unsanitized): -//│ let bar, foo, bar$, foo$capture1; +//│ let foo; +//│ let bar, bar$, foo$capture1; //│ bar$ = function bar$(foo$capture2) { //│ return foo$capture2.x$capture$0 //│ }; @@ -32,7 +33,8 @@ fun foo() = //│ static [definitionMetadata] = ["class", "foo$capture"]; //│ }); //│ foo = function foo() { -//│ let tmp, capture, bar$here; +//│ let x, tmp; +//│ let capture, bar$here; //│ capture = new foo$capture1(null); //│ capture.x$capture$0 = 1; //│ bar$here = runtime.safeCall(bar(capture)); @@ -63,11 +65,34 @@ xs.0() //│ = 2 +:fixme fun foo() = let x fun bar() = x = 2 [bar, () => x] +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'x' +//│ ║ l.73: [bar, () => x] +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.70: let x +//│ ╙── ^ + +:fixme +:expect 2 +let b_x = foo() +b_x.0() +b_x.1() +//│ ═══[RUNTIME ERROR] ReferenceError: x is not defined +//│ ═══[RUNTIME ERROR] Expected: '2', got: 'undefined' +//│ b_x = [fun bar, fun] + + +fun foo() = + let x = undefined + fun bar() = + x = 2 + [bar, () => x] :expect 2 let b_x = foo() @@ -84,8 +109,9 @@ fun foo() = x bar //│ JS (unsanitized): -//│ let bar3, foo3; -//│ bar3 = function bar() { let x; x = undefined; return x }; -//│ foo3 = function foo() { return bar3 }; +//│ let foo4; +//│ let bar4; +//│ bar4 = function bar() { let x; return x }; +//│ foo4 = function foo() { return bar4 }; diff --git a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls index 1ccd5b8b7e..f10ee93f59 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/StackSafetyLift.mls @@ -24,7 +24,8 @@ fun hi(n) = else hi(n - 1) hi(0) //│ JS (unsanitized): -//│ let hi, Cont$func$hi$1, res, $_stack$_safe$_body$_, doUnwind$, Cont$func$hi$$; +//│ let hi; +//│ let Cont$func$hi$1, res, $_stack$_safe$_body$_, doUnwind$, Cont$func$hi$$; //│ Cont$func$hi$$ = function Cont$func$hi$$(isMut, n, pc) { //│ let tmp, tmp1; //│ if (isMut === true) { @@ -63,7 +64,8 @@ hi(0) //│ return res1 //│ }; //│ hi = function hi(n) { -//│ let scrut, tmp, stackDelayRes; +//│ let scrut, tmp; +//│ let stackDelayRes; //│ runtime.stackDepth = runtime.stackDepth + 1; //│ stackDelayRes = runtime.checkDepth(); //│ if (stackDelayRes instanceof runtime.EffectSig.class) { @@ -95,7 +97,8 @@ fun sum(n) = n + sum(n - 1) sum(10000) //│ JS (unsanitized): -//│ let sum1, Cont$func$sum$1, res1, $_stack$_safe$_body$_1, doUnwind$1, Cont$func$sum$$; +//│ let sum1; +//│ let Cont$func$sum$1, res1, $_stack$_safe$_body$_1, doUnwind$1, Cont$func$sum$$; //│ Cont$func$sum$$ = function Cont$func$sum$$(isMut, n, tmp, pc) { //│ let tmp1, tmp2; //│ if (isMut === true) { @@ -154,7 +157,8 @@ sum(10000) //│ return res2 //│ }; //│ sum1 = function sum(n) { -//│ let scrut, tmp, tmp1, curDepth, stackDelayRes; +//│ let scrut, tmp, tmp1; +//│ let curDepth, stackDelayRes; //│ curDepth = runtime.stackDepth; //│ runtime.stackDepth = runtime.stackDepth + 1; //│ stackDelayRes = runtime.checkDepth(); diff --git a/hkmc2/shared/src/test/mlscript/llir/BasicCpp.mls b/hkmc2/shared/src/test/mlscript/llir/BasicCpp.mls index 50b35ad37e..629dad4605 100644 --- a/hkmc2/shared/src/test/mlscript/llir/BasicCpp.mls +++ b/hkmc2/shared/src/test/mlscript/llir/BasicCpp.mls @@ -3,7 +3,7 @@ :cpp fun foo(a) = - let x + let x = undefined if a > 0 do x = 1 x + 1 diff --git a/hkmc2/shared/src/test/mlscript/objbuf/Mutation.mls b/hkmc2/shared/src/test/mlscript/objbuf/Mutation.mls index 449569ce95..dd3df20511 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/Mutation.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/Mutation.mls @@ -42,7 +42,8 @@ class A(x) with //│ } //│ static f(buf, idx) { //│ return (y) => { -//│ let tmp, tmp1, idx1, idx2, idx3, idx4, idx5; +//│ let tmp, tmp1; +//│ let idx1, idx2, idx3, idx4, idx5; //│ idx1 = idx + 0; //│ tmp = buf.buf.at(idx1) + 1; //│ idx5 = idx + 0; diff --git a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls index a37c8bf69e..abf6f1e9bb 100644 --- a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls @@ -135,8 +135,8 @@ fun popByIndex(start, end, acc, lft) = //│ JS (unsanitized): //│ let popByIndex; //│ popByIndex = function popByIndex(start, end, acc, lft) { -//│ let scrut, tmp34, tmp35, tmp36; //│ loopLabel: while (true) { +//│ let scrut, tmp34, tmp35, tmp36; //│ scrut = start >= end; //│ if (scrut === true) { //│ return acc diff --git a/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls b/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls index ee607e3a54..7b0cc64c60 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/Simple.mls @@ -5,7 +5,7 @@ @tailrec fun f = f //│ JS (unsanitized): -//│ let f; f = function f() { loopLabel: while (true) { continue loopLabel; break; } }; +//│ let f; f = function f() { loopLabel: while (true) { let tmp; continue loopLabel; break; } }; :fixme module Foo with @@ -29,8 +29,14 @@ fun f(x) = f(f(x) + 1) //│ JS (unsanitized): //│ let f3; //│ f3 = function f(x) { -//│ let tmp, tmp1; -//│ loopLabel: while (true) { tmp = f3(x); tmp1 = tmp + 1; x = tmp1; continue loopLabel; break; } +//│ loopLabel: while (true) { +//│ let tmp, tmp1; +//│ tmp = f3(x); +//│ tmp1 = tmp + 1; +//│ x = tmp1; +//│ continue loopLabel; +//│ break; +//│ } //│ }; module A with @@ -44,7 +50,7 @@ module A with module B with fun g(x) = A.f(x * 2) //│ ╔══[WARNING] This function does not directly self-recurse, but is marked @tailrec. -//│ ║ l.43: fun f(x) = B.g(x + 1) +//│ ║ l.49: fun f(x) = B.g(x + 1) //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls index 146a74c7e1..b998753c3b 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/TailRecOpt.mls @@ -16,18 +16,20 @@ fun f(a, b, c) = else c f(10000, 20000, 0) //│ JS (unsanitized): -//│ let f, g, g_f; +//│ let f, g; +//│ let g_f; //│ g_f = function g_f(id, param0, param1, param2, param3) { -//│ let scrut, scrut1, tmp, tmp1, tmp2; //│ loopLabel: while (true) { //│ switch (id) { //│ case 0: +//│ let tmp; //│ tmp = param2 + param3; //│ param2 = tmp; //│ id = 1; //│ continue loopLabel; //│ break; //│ case 1: +//│ let scrut, scrut1, tmp1, tmp2; //│ scrut = param0 > 0; //│ if (scrut === true) { //│ tmp1 = param0 - 1; @@ -75,8 +77,8 @@ A.sum(20000) //│ runtime.Unit; //│ } //│ static sum_impl(n, acc) { -//│ let scrut, tmp, tmp1; //│ loopLabel: while (true) { +//│ let scrut, tmp, tmp1; //│ scrut = Predef.equals(n, 0); //│ if (scrut === true) { //│ return acc @@ -146,7 +148,7 @@ fun f(x) = @tailcall f(x) @tailcall g() //│ ╔══[ERROR] This call is not in tail position. -//│ ║ l.145: @tailcall f(x) +//│ ║ l.147: @tailcall f(x) //│ ╙── ^ :lift @@ -164,10 +166,10 @@ module A with @tailcall g(x - 1) A.f(10000) //│ ╔══[ERROR] This tail call exits the current scope is not optimized. -//│ ║ l.163: fun g(x) = if x < 0 then 0 else @tailcall f(x) +//│ ║ l.165: fun g(x) = if x < 0 then 0 else @tailcall f(x) //│ ╙── ^^^ //│ ╔══[ERROR] This tail call exits the current scope is not optimized. -//│ ║ l.164: @tailcall g(x - 1) +//│ ║ l.166: @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ═══[RUNTIME ERROR] RangeError: Maximum call stack size exceeded @@ -194,12 +196,12 @@ class A with fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) (new A).f(10) //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.193: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.195: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^^^^^^^^ //│ ╔══[ERROR] Class methods may not yet be marked @tailrec. -//│ ║ l.193: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) +//│ ║ l.195: @tailrec fun f(x) = if x == 0 then 0 else @tailcall g(x - 1) //│ ╙── ^ //│ ╔══[ERROR] Calls from class methods cannot yet be marked @tailcall. -//│ ║ l.194: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) +//│ ║ l.196: fun g(x) = if x == 0 then 1 else @tailcall f(x - 1) //│ ╙── ^^^^^^^^ //│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls index 1c74e8bfff..7907a829f7 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls @@ -25,17 +25,17 @@ fun test(x) = :sjs true and test(42) //│ JS (unsanitized): -//│ let scrut4, scrut5, tmp4; -//│ split_root$2: { -//│ split_1$2: { -//│ scrut4 = true; -//│ if (scrut4 === true) { -//│ scrut5 = test(42); -//│ if (scrut5 === true) { +//│ let scrut6, scrut7, tmp4; +//│ split_root$3: { +//│ split_1$3: { +//│ scrut6 = true; +//│ if (scrut6 === true) { +//│ scrut7 = test(42); +//│ if (scrut7 === true) { //│ tmp4 = true; -//│ break split_root$2 -//│ } else { break split_1$2 } -//│ } else { break split_1$2 } +//│ break split_root$3 +//│ } else { break split_1$3 } +//│ } else { break split_1$3 } //│ } //│ tmp4 = false; //│ } diff --git a/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls b/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls index b3a50c0a6f..a52417db68 100644 --- a/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls +++ b/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls @@ -21,7 +21,8 @@ pattern SumPair = Pair(a, b) => a + b //│ globalThis.Object.freeze(this); //│ } //│ unapply(input) { -//│ let transform, argument0$, argument1$, transformResult, tmp, lambda; +//│ let transform, argument0$, argument1$, transformResult, tmp; +//│ let lambda; //│ lambda = (undefined, function (a, b) { //│ return a + b //│ }); diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 7c71cd21c9..4404c4cbe1 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -114,11 +114,12 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: case Return(res, implct) => assert(implct) Assign(resSym, res, Return(Value.Lit(syntax.Tree.UnitLit(false)), true)) + case _: Scoped => ??? // impossible. mapTail has handled this case specially case tl: (Throw | Break | Continue) => tl ) if showLoweredTree.isSet then output(s"Lowered:") - output(le.showAsTree) + output(lowered0.showAsTree) // * We used to do this to avoid needlessly generating new variable names in separate blocks: // val nestedScp = baseScp.nest diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala index 0fbbce6bd1..14dfbb13a2 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala @@ -71,7 +71,7 @@ abstract class MLsDiffMaker extends DiffMaker: val liftDefns = NullaryCommand("lift") val importQQ = NullaryCommand("qq") val stageCode = NullaryCommand("staging") - val dontRewriteWhile = NullaryCommand("dontRewriteWhile") + val rewriteWhile = NullaryCommand("rewriteWhile") val noTailRecOpt = NullaryCommand("noTailRec") def mkConfig: Config = @@ -100,7 +100,7 @@ abstract class MLsDiffMaker extends DiffMaker: liftDefns = Opt.when(liftDefns.isSet)(LiftDefns()), stageCode = stageCode.isSet, target = if wasm.isSet then CompilationTarget.Wasm else CompilationTarget.JS, - rewriteWhileLoops = !dontRewriteWhile.isSet, + rewriteWhileLoops = rewriteWhile.isSet, tailRecOpt = !noTailRecOpt.isSet, )