diff --git a/hkmc2/shared/src/main/scala/hkmc2/Config.scala b/hkmc2/shared/src/main/scala/hkmc2/Config.scala index af1edfcf76..baa8382727 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/Config.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/Config.scala @@ -71,7 +71,7 @@ object Config: tailRecOpt = true, deforest = N, etaExpansion = S(EtaExpansion.default), - inlining = S(Inliner(10)), + inlining = S(Inliner(default.inlineThreshold)), deadBranchRemoval = default.deadBranchRemoval, qqEnabled = false, funcToCls = false, @@ -83,6 +83,7 @@ object Config: object default: val patMatConsequentSharingThreshold = S(15) val deadBranchRemoval = false // TODO + val inlineThreshold = 10 case class SanityChecks(light: Bool, checkUnreachable: Bool) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala index 51e5c1e199..8861c2b366 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala @@ -1108,7 +1108,7 @@ class BlockSimplifier case _ => super.applyBlock(b) def applyBlock(blk: Block) = - Label(lblSym, false, Copier.applyBlock(blk), _) + Label(lblSym, false, Copier.apply(blk), _) class Transformer(m: InlinerMap) extends BlockTransformer(SymbolSubst()): diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 1b0e77ecd8..a1ef76af45 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -117,7 +117,20 @@ class BlockTransformer(subst: SymbolSubst): def applyScopedBlock(b: Block): Block = b match case Scoped(s, bd) => val nb = applySubBlock(bd) - if nb is bd then b else Scoped(s, nb) + + // Set does not have .mapConserve + var hasDiff = false + val ns = s.map[ScopedSymbol]: + case s: LocalVarSymbol => + val ns = s.subst + if ns isnt s then hasDiff = true + ns + case s: BlockMemberSymbol => + val ns = s.subst + if ns isnt s then hasDiff = true + ns + + if (nb is bd) && !hasDiff then b else Scoped(ns, nb) case _ => applySubBlock(b) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala index 28b4d18f25..c467446762 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala @@ -355,7 +355,7 @@ class Rewrite(val deadParamElimSolver: DeadParamElimSolver)(using Raise): val filteredParams = filterFunParams(fDefn.dSym, fDefn.params, instId) val transformedBody = new Rewriter(instId).rewriteFunBody(fDefn.dSym, fDefn.params, fDefn.body) val (refreshedParams, refreshParamMap) = makeRefreshedParams(filteredParams) - val bodyWithCorrectSymbols = new RefreshSymbol(refreshParamMap).applyBlock(transformedBody) + val bodyWithCorrectSymbols = new RefreshSymbol(refreshParamMap).apply(transformedBody) FunDefn( N, bms, tSym, refreshedParams, bodyWithCorrectSymbols)(fDefn.configOverride, fDefn.annotations) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/SymbolRefresher.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/SymbolRefresher.scala index 8a7892d0d7..86c635972b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/SymbolRefresher.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/SymbolRefresher.scala @@ -9,297 +9,154 @@ import hkmc2.utils.* import semantics.* import semantics.Elaborator.State -class SymbolRefresher(existingMapping: Map[Symbol, Symbol])(using State) extends BlockTransformer(SymbolSubst.Id): - val mapping = MutMap.from(existingMapping) - // Stack of cleanup frames, one per scoped block. ClsLikeDefn / applyObjBody add - // class-internal symbols to the top frame; the enclosing applyScopedBlock pops - // and removes them from `mapping` when its body finishes. Initialized with a - // bottom frame so callers that hand us a non-Scoped entry block still have a - // valid `cleanupStack.head` to add to (the bottom frame is never popped). - private var toRemoveSymbols: List[MutSet[Symbol]] = MutSet.empty[Symbol] :: Nil +class SymbolRefresherWalker(mapping: MutMap[Symbol, Symbol])(using State) extends BlockTraverser: - override def applyScopedBlock(b: Block): Block = - toRemoveSymbols = MutSet.empty[Symbol] :: toRemoveSymbols - val res = b match - case Scoped(syms, body) => - val newSyms = MutSet.empty[ScopedSymbol] - val oldSyms = MutSet.empty[Symbol] - for s <- syms.toList.sortBy(_.uid) do - assert(!mapping.isDefinedAt(s), s"already defined: $s") - val new_s: ScopedSymbol = s match - case tmpSym: TempSymbol => new TempSymbol(N, tmpSym.nme) - case bms: BlockMemberSymbol => - val newBms = new BlockMemberSymbol(bms.nme, Nil, bms.nameIsMeaningful) - newBms.tsym = bms.tsym.map: t => - val newOwner: Opt[InnerSymbol] = t.owner.map: o => - existingMapping.get(o) match - case Some(inner: InnerSymbol) => inner - case _ => o - val nt = new TermSymbol(t.k, newOwner, t.id) - mapping(t) = nt - oldSyms.add(t) - nt - newBms - case varSym: VarSymbol => new VarSymbol(varSym.id) - mapping(s) = new_s - oldSyms.add(s) - newSyms.add(new_s) - val r = Scoped(newSyms, applyBlock(body)) - for s <- oldSyms do mapping.remove(s) - r - case _ => super.applyScopedBlock(b) - val hd = toRemoveSymbols.head - toRemoveSymbols = toRemoveSymbols.tail - hd.foreach(mapping.remove) - res - override def applyBlock(b: Block): Block = - b match - case Assign(lhs, rhs, rest) => - applyResult(rhs): newRhs => - val newLhs: Assignable = lhs match - case lhs: NoSymbol => lhs - case lhs: LocalVarSymbol => - mapping.get(lhs) match - case Some(sym: LocalVarSymbol) => sym - case Some(sym) => lastWords(s"assignment local ${lhs.nme} mapped to non-variable ${sym.nme}") - case None => lhs - val newRest = applyBlock(rest) - if (newLhs is lhs) && (newRhs is rhs) && (newRest is rest) then b else Assign(newLhs, newRhs, newRest) - case Label(label, loop, body, rest) => - assert(!mapping.isDefinedAt(label)) - val newLabel = new LabelSymbol(label.trm, label.nme) - mapping(label) = newLabel - val newBody = applyBlock(body) - mapping.remove(label) - val newRest = applyBlock(rest) - Label(newLabel, loop, newBody, newRest) - case Break(label) => Break(mapping.getOrElse(label, label).asInstanceOf[LabelSymbol]) - case Continue(label) => Continue(mapping.getOrElse(label, label).asInstanceOf[LabelSymbol]) - case _ => super.applyBlock(b) + private def assertUpdate[T <: Symbol](k: T, v: T) = + assert(!mapping.isDefinedAt(k), s"already defined: $k") + mapping(k) = v - override def applyDefn(defn: Defn)(k: Defn => Block): Block = - defn match - case fun: FunDefn => - assert(fun.owner.isEmpty) - // because fun sym is not treated as a free var, we refresh here - var newlyCreated = false - val (sym2, dSym2) = mapping.get(fun.sym) match - case Some(s: BlockMemberSymbol) => (s, s.tsym.get) - case None => - newlyCreated = true - val newBms = new BlockMemberSymbol(fun.sym.nme, fun.sym.trees, fun.sym.nameIsMeaningful) - val newDsym = fun.sym.tsym.map: tsym => - assert(tsym.owner.isEmpty) - new TermSymbol(tsym.k, N, tsym.id) - newBms.tsym = S(newDsym.get) - // Keep the definition symbol in sync with the freshly-created member symbol. - // Self-recursive references use the disambiguating TermSymbol, and later passes - // such as inlining rely on that symbol to identify the function being called. - mapping(fun.dSym) = newDsym.get - mapping(fun.sym) = newBms - (newBms, newDsym.get) - case _ => die - val oldParamSyms = Buffer.empty[VarSymbol] - val params2 = fun.params.map: - case ParamList(flags, params, restParam) => - def handleSingleParam(p: Param) = - val Param(flags, sym, sign, modulefulness) = p - oldParamSyms.append(sym) - val newSym = new VarSymbol(sym.id) - assert(!mapping.isDefinedAt(sym)) - mapping(sym) = newSym - Param(flags, newSym, sign, modulefulness) - val params2 = params.map(handleSingleParam) - val rest2 = restParam.map(handleSingleParam) - ParamList(flags, params2, rest2) - val body2 = applyFunBodyLikeBlock(fun.body) - for s <- oldParamSyms do mapping.remove(s) - if newlyCreated then - Scoped(Set.single(sym2), k(FunDefn(N, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations))) - else - k(FunDefn(N, sym2, dSym2, params2, body2)(fun.configOverride, fun.annotations)) - case defn @ ValDefn(tsym, sym, rhs) => - val (tsym2, sym2) = mapping.get(sym) match - case None => - val newBms = new BlockMemberSymbol(sym.nme, sym.trees, sym.nameIsMeaningful) - val newOwner = tsym.owner.map: o => - existingMapping.get(o) match - case Some(inner: InnerSymbol) => inner - case _ => o - val newTsym = new TermSymbol(tsym.k, newOwner, tsym.id) - newBms.tsym = S(newTsym) - (newTsym, newBms) - case S(bms: BlockMemberSymbol) => - (bms.tsym.get, bms) - case _ => die - applyPath(rhs): rhs2 => - k(ValDefn(tsym2, sym2, rhs2)(defn.configOverride, defn.annotations)) - case defn: ClsLikeDefn => - val hd = toRemoveSymbols.head - val oldIsym = defn.isym - assert(!mapping.isDefinedAt(oldIsym), s"isym already in mapping: $oldIsym") - val newIsym: DefinitionSymbol[? <: ClassLikeDef] & InnerSymbol = oldIsym match - case c: ClassSymbol => new ClassSymbol(c.tree, c.id) - case m: ModuleOrObjectSymbol => new ModuleOrObjectSymbol(m.tree, m.id) - case p: PatternSymbol => new PatternSymbol(p.id, p.params, p.body) - case _ => lastWords(s"unexpected isym kind: $oldIsym") - mapping(oldIsym) = newIsym - hd += oldIsym - - var newlyCreatedSym = false - val newSym: BlockMemberSymbol = mapping.get(defn.sym) match - case Some(bms: BlockMemberSymbol) => bms - case None => - newlyCreatedSym = true - val nb = new BlockMemberSymbol(defn.sym.nme, Nil, defn.sym.nameIsMeaningful) - mapping(defn.sym) = nb - nb - case _ => die + private def refreshTempSymbol(s: TempSymbol) = + assertUpdate(s, new TempSymbol(s.trm, s.nme)) - val newCtorSym: Opt[ClassCtorSymbol] = defn.ctorSym.map: cs => - assert(!mapping.isDefinedAt(cs)) - val newOwner = newIsym match - case cls: ClassSymbol => cls - case _ => lastWords(s"ClassCtorSymbol for non-class: $newIsym") - val ncs = new ClassCtorSymbol(cs.k, S(newOwner), cs.id) - mapping(cs) = ncs - hd += cs - ncs + private def refreshVarSymbol(s: VarSymbol) = + assertUpdate(s, new VarSymbol(s.id)) + + private def refreshBlockMemberSymbol(s: BlockMemberSymbol) = + assertUpdate(s, new BlockMemberSymbol(s.nme, s.trees, s.nameIsMeaningful)) - val newOwn: Opt[InnerSymbol] = defn.owner.map: o => - mapping.get(o) match - case Some(inner: InnerSymbol) => inner - case _ => o + private def refreshLabelSymbol(s: LabelSymbol) = + assertUpdate(s, new LabelSymbol(s.trm, s.nme)) - def freshenParamList(pl: ParamList): ParamList = - def handleParam(p: Param) = - val ns = new VarSymbol(p.sym.id) - assert(!mapping.isDefinedAt(p.sym)) - mapping(p.sym) = ns - hd += p.sym - Param(p.flags, ns, p.sign, p.modulefulness) - ParamList(pl.flags, pl.params.map(handleParam), pl.restParam.map(handleParam)) - val newParamsOpt = defn.paramsOpt.map(freshenParamList) - val newAuxParams = defn.auxParams.map(freshenParamList) + private def refreshTermSymbol(s: TermSymbol) = + // Inner symbol (if present) must be traversed at this point. + assertUpdate(s, new TermSymbol(s.k, s.owner.map(o => mapping.getOrElse(o, o).asInstanceOf[InnerSymbol]), s.id)) - val newPrivateFields = freshenPrivateFields(defn.privateFields, newIsym) - val newPublicFields = freshenPublicFields(defn.publicFields, newIsym) - val newMethods = freshenMethods(defn.methods, oldIsym, newIsym) + private def refreshClassSymbol(s: ClassSymbol) = + val ns = new ClassSymbol(s.tree, s.id) + assertUpdate(s, ns) + // defn is relied by JSBuilder to identify whether a class should be lifted + ns.defn = s.defn - val newPreCtor = applyFunBodyLikeBlock(defn.preCtor) - val newCtor = applyFunBodyLikeBlock(defn.ctor) - val newMod = defn.companion.map(applyObjBody) + private def refreshModuleOrObjectSymbol(s: ModuleOrObjectSymbol) = + assertUpdate(s, new ModuleOrObjectSymbol(s.tree, s.id)) - def buildResult(newParentPath: Opt[Path]): Block = - val newDefn = ClsLikeDefn(newOwn, newIsym, newSym, newCtorSym, defn.k, - newParamsOpt, newAuxParams, newParentPath, newMethods, - newPrivateFields, newPublicFields, newPreCtor, newCtor, newMod, defn.bufferable)(defn.configOverride, defn.annotations) - if newlyCreatedSym then - Scoped(Set.single(newSym), k(newDefn)) - else - k(newDefn) + private def refreshPatternSymbol(s: PatternSymbol) = + assertUpdate(s, new PatternSymbol(s.id, s.params, s.body)) - defn.parentPath match - case Some(pp) => applyPath(pp): pp2 => - buildResult(if pp2 is pp then defn.parentPath else Some(pp2)) - case None => buildResult(None) - - override def applyPath(p: Path)(k: Path => Block): Block = p match - case p @ Select(qual, name) => - applyPath(qual): qual2 => - val sym2 = p.symbol.map: s => - mapping.get(s) match - case Some(ds: DefinitionSymbol[?]) => ds - case _ => s - k(if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2).withLocOf(p)) - case _ => super.applyPath(p)(k) + private def refreshTopLevelSymbol(s: TopLevelSymbol) = + assertUpdate(s, new TopLevelSymbol(s.nme)) - override def applyValue(v: Value)(k: Value => Block): Block = v match - case Value.SimpleRef(l) => - mapping.get(l) match - case Some(newSym: SimpleSymbol) => - k(newSym.asSimpleRef) - case _ => super.applyValue(v)(k) - case Value.MemberRef(bms, disamb) => - mapping.get(bms) match - case Some(newBms: BlockMemberSymbol) => - val newDisamb = mapping.get(disamb) match - case Some(nd: DefinitionSymbol[?]) => nd - case Some(nd) => lastWords(s"unexpected symbol kind for disamb: ${nd}") - case N => lastWords(s"unexpected lack of refreshed disamb symbol for $disamb") - k(newBms.asMemberRef(newDisamb)) - case Some(newSym: (LocalVarSymbol | TempSymbol)) => k(newSym.asSimpleRef) - case _ => super.applyValue(v)(k) - case Value.This(sym) => - mapping.get(sym) match - case Some(inner: InnerSymbol) => k(inner.asThis.withLocOf(v)) - case _ => super.applyValue(v)(k) - case _ => super.applyValue(v)(k) - - private def freshenPrivateFields( - fields: Ls[TermSymbol], ownerIsym: InnerSymbol - ): Ls[TermSymbol] = fields.map: ts => - assert(!mapping.isDefinedAt(ts)) - val nts = new TermSymbol(ts.k, S(ownerIsym), ts.id) - mapping(ts) = nts - toRemoveSymbols.head += ts - nts + private def refreshClassCtorSymbol(s: ClassCtorSymbol) = + assertUpdate(s, new ClassCtorSymbol(s.k, S(mapping.getOrElse(s.owner.value, s.owner.value).asInstanceOf[ClassSymbol]), s.id)) - private def freshenPublicFields( - fields: Ls[BlockMemberSymbol -> TermSymbol], ownerIsym: InnerSymbol - ): Ls[BlockMemberSymbol -> TermSymbol] = fields.map: - case (bms, ts) => - assert(!mapping.isDefinedAt(bms)) - assert(!mapping.isDefinedAt(ts)) - val nbms = new BlockMemberSymbol(bms.nme, Nil, bms.nameIsMeaningful) - val nts = new TermSymbol(ts.k, S(ownerIsym), ts.id) - nbms.tsym = S(nts) - mapping(bms) = nbms - mapping(ts) = nts - toRemoveSymbols.head.addAll(Seq(bms, ts)) - nbms -> nts + private def refreshParamList(pl: ParamList) = + for + p <- pl.restParam ++: pl.params + do + refreshVarSymbol(p.sym) - private def freshenMethods( - methods: Ls[FunDefn], oldIsym: InnerSymbol, newIsym: InnerSymbol - ): Ls[FunDefn] = - val methodsAndNewSyms = methods.map: m => - assert(m.owner.contains(oldIsym), s"method owner mismatch: ${m.owner} vs S($oldIsym)") - assert(!mapping.isDefinedAt(m.sym)) - assert(!mapping.isDefinedAt(m.dSym)) - val newMsym = new BlockMemberSymbol(m.sym.nme, Nil, m.sym.nameIsMeaningful) - val newDsym = new TermSymbol(m.dSym.k, S(newIsym), m.dSym.id) - newMsym.tsym = S(newDsym) - mapping(m.sym) = newMsym - mapping(m.dSym) = newDsym - toRemoveSymbols.head.addAll(Seq(m.sym, m.dSym)) - (m, newMsym, newDsym) - methodsAndNewSyms.map: (m, newMsym, newDsym) => - val methodParamOlds = MutSet.empty[VarSymbol] - val newParams = m.params.map: pl => - def handleParam(p: Param) = - val ns = new VarSymbol(p.sym.id) - assert(!mapping.isDefinedAt(p.sym)) - mapping(p.sym) = ns - methodParamOlds += p.sym - Param(p.flags, ns, p.sign, p.modulefulness) - ParamList(pl.flags, pl.params.map(handleParam), pl.restParam.map(handleParam)) - val newBody = applyFunBodyLikeBlock(m.body) - methodParamOlds.foreach(mapping.remove) - FunDefn(S(newIsym), newMsym, newDsym, newParams, newBody)(m.configOverride, m.annotations) + override def applyBlock(b: Block) = b match + case Scoped(syms, body) => + for s <- syms.toList.sortBy(_.uid) do + s match + case s: TempSymbol => refreshTempSymbol(s) + case s: VarSymbol => refreshVarSymbol(s) + case s: BlockMemberSymbol => refreshBlockMemberSymbol(s) + applyBlock(body) + case Label(label, loop, body, rest) => + refreshLabelSymbol(label) + applyBlock(body) + applyBlock(rest) + case _ => super.applyBlock(b) - override def applyObjBody(defn: ClsLikeBody): ClsLikeBody = - val hd = toRemoveSymbols.head - val oldIsym = defn.isym - assert(!mapping.isDefinedAt(oldIsym), s"companion isym already in mapping: $oldIsym") - val newIsym: DefinitionSymbol[? <: ModuleOrObjectDef] & InnerSymbol = oldIsym match - case m: ModuleOrObjectSymbol => new ModuleOrObjectSymbol(m.tree, m.id) - case _ => lastWords(s"unexpected companion isym kind: $oldIsym") - mapping(oldIsym) = newIsym - hd += oldIsym - - val newPrivateFields = freshenPrivateFields(defn.privateFields, newIsym) - val newPublicFields = freshenPublicFields(defn.publicFields, newIsym) - val newMethods = freshenMethods(defn.methods, oldIsym, newIsym) - val newCtor = applyFunBodyLikeBlock(defn.ctor) - - ClsLikeBody(newIsym, newMethods, newPrivateFields, newPublicFields, newCtor, defn.annotations) + override def applyFunDefn(fun: FunDefn): Unit = + val FunDefn(owner, sym, dSym, params, body) = fun + assert(mapping.isDefinedAt(sym), s"BlockMemberSymbol ${sym} for FunDefn is a free variable for this block") + refreshTermSymbol(dSym) + params.foreach(refreshParamList) + applyBlock(body) + + override def applyValDefn(defn: ValDefn): Unit = + val ValDefn(tsym, sym, result) = defn + assert(mapping.isDefinedAt(sym), s"BlockMemberSymbol ${sym} for ValDefn is a free variable for this block") + refreshTermSymbol(tsym) + applyResult(result) + + override def applyClsLikeDefn(defn: ClsLikeDefn): Unit = + val ClsLikeDefn( + owner, isym, sym, ctorSym, k, paramsOpt, auxParams, parentPath, + methods, privateFields, publicFields, preCtor, ctor, companion, + bufferable) = defn + assert(mapping.isDefinedAt(sym), s"BlockMemberSymbol ${sym} for ClsLikeDefn is a free variable for this block") + isym match + case s: ClassSymbol => refreshClassSymbol(s) + case s: ModuleOrObjectSymbol => refreshModuleOrObjectSymbol(s) + case s: PatternSymbol => refreshPatternSymbol(s) + case s: TopLevelSymbol => refreshTopLevelSymbol(s) + ctorSym.foreach(refreshClassCtorSymbol) + paramsOpt.foreach(refreshParamList) + auxParams.foreach(refreshParamList) + methods.foreach: mtd => + // For methods, the BlockMemberSymbol is owned by the class itself + refreshBlockMemberSymbol(mtd.sym) + applyFunDefn(mtd) + privateFields.foreach(refreshTermSymbol) + publicFields.foreach: p => + refreshBlockMemberSymbol(p._1) + // For public fields, we have ValDefn for defining the variable + // refreshTermSymbol(p._2) + applyBlock(preCtor) + applyBlock(ctor) + companion.foreach(applyClsLikeBody) + + def applyClsLikeBody(b: ClsLikeBody): Unit = + val ClsLikeBody( + isym, methods, privateFields, publicFields, ctor, annotations + ) = b + isym match + case s: ModuleOrObjectSymbol => refreshModuleOrObjectSymbol(s) + case s: TopLevelSymbol => refreshTopLevelSymbol(s) + methods.foreach: mtd => + // For methods, the BlockMemberSymbol is owned by the class itself + refreshBlockMemberSymbol(mtd.sym) + applyFunDefn(mtd) + privateFields.foreach(refreshTermSymbol) + publicFields.foreach: p => + refreshBlockMemberSymbol(p._1) + // For public fields, we have ValDefn for defining the variable + // refreshTermSymbol(p._2) + applyBlock(ctor) + +object SymbolRefresher: + + def initSymbolSubst(m: collection.Map[Symbol, Symbol]) = + new SymbolSubst: + override def mapBlockMemberSym(s: BlockMemberSymbol): BlockMemberSymbol = m.getOrElse(s, s).asInstanceOf[BlockMemberSymbol] + override def mapTempSym(s: TempSymbol): TempSymbol = m.getOrElse(s, s).asInstanceOf[TempSymbol] + override def mapVarSym(s: VarSymbol): VarSymbol = m.getOrElse(s, s).asInstanceOf[VarSymbol] + override def mapTermSym(s: TermSymbol): TermSymbol = m.getOrElse(s, s).asInstanceOf[TermSymbol] + override def mapClassCtorSym(s: ClassCtorSymbol): ClassCtorSymbol = m.getOrElse(s, s).asInstanceOf[ClassCtorSymbol] + override def mapClsSym(s: ClassSymbol): ClassSymbol = m.getOrElse(s, s).asInstanceOf[ClassSymbol] + override def mapModuleSym(s: ModuleOrObjectSymbol): ModuleOrObjectSymbol = m.getOrElse(s, s).asInstanceOf[ModuleOrObjectSymbol] + override def mapPatSym(s: PatternSymbol): PatternSymbol = m.getOrElse(s, s).asInstanceOf[PatternSymbol] + override def mapTopLevelSym(s: TopLevelSymbol): TopLevelSymbol = m.getOrElse(s, s).asInstanceOf[TopLevelSymbol] + override def mapLabelSym(s: LabelSymbol): LabelSymbol = m.getOrElse(s, s).asInstanceOf[LabelSymbol] + +// An internal class so that the actual map can be used +private class SymbolRefresherInternal(m: MutMap[Symbol, Symbol])(using State) extends BlockTransformer(SymbolRefresher.initSymbolSubst(m)): + // We have a pretty weird setup here, where we store a mutable state inside the SymbolRefresher + // We must initialize the SymbolRefresher by walking before applyBlock + def apply(b: Block) = + SymbolRefresherWalker(m).applyBlock(b) + applyBlock(b) + + // Although the types created during walking can always be symbol substituted, the user may pass in extra symbol that map across different types + override def applySimpleSymbol(s: SimpleSymbol): SimpleSymbol = m.getOrElse(s, s).asInstanceOf[SimpleSymbol] + + override def applyImportSymbol(s: ImportSymbol): ImportSymbol = m.getOrElse(s, s).asInstanceOf[ImportSymbol] + + override def applyAssignLhs(s: Assignable): Assignable = s match + case s: NoSymbol => s + case s: LocalVarSymbol => m.getOrElse(s, s).asInstanceOf[LocalVarSymbol] + +class SymbolRefresher(m: Map[Symbol, Symbol])(using State) extends SymbolRefresherInternal(MutMap.from(m)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala index 759926e1f7..dfac780fd2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -485,8 +485,36 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): case Some(bms) => k(bms.asMemberRef(l.asMod.get)) case None => super.applyValue(v)(k) + // We may generate bms -> VarSymbol replacement, which require change of IR + case Value.MemberRef(bms, disamb) if existingMapping.contains(bms) => + val sym = existingMapping(bms) + sym match + case s: VarSymbol => k(Value.SimpleRef(s)) + case _ => super.applyValue(v)(k) case _ => super.applyValue(v)(k) end RefreshSymbol + + // FIXME: This is a temporary workaround for the deforestation problem discussed at + // https://discord.com/channels/884326249617559653/935507764384501760/1512041398520774832 + // It should be fixed after we fix Lowering; then, this function should be removed, + // and its uses replaced back by `new RefreshSymbol(refreshParamMap.toMap).apply` + // TODO: it may cause BMS to malfunction if both class and function refer to same BMS and they are split apart + def refreshExtractedBody(mapping: Map[Symbol, Symbol], body: Block): Block = + val defined = MutSet.empty[ScopedSymbol] + val missing = MutSet.empty[ScopedSymbol] + val walker = new BlockTraverserShallow: + override def applyBlock(b: Block): Unit = b match + case Scoped(syms, body) => + defined.addAll(syms) + applyBlock(body) + case Define(defn: FunDefn, rest) => + if !defined(defn.sym) then + missing.add(defn.sym) + defined.add(defn.sym) + applyBlock(rest) + case _ => super.applyBlock(b) + walker.applyBlock(body) + new RefreshSymbol(mapping).apply(Scoped(missing, body)) // Instantiated polymorphic functions val newPolyFuns = @@ -507,7 +535,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness), pl.restParam) - val bodyWithCorrectSymbols = new RefreshSymbol(refreshParamMap.toMap).applyBlock(transformedBody) + val bodyWithCorrectSymbols = refreshExtractedBody(refreshParamMap.toMap, transformedBody) FunDefn( N, bms, tSym, refreshedParams, bodyWithCorrectSymbols)(N, PrivateModifier :: fDefn.annotations) @@ -524,7 +552,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): new Rewriter(instId).applyBlock(ogBody), Return(mkCall(restFunSym, restFunArgs))) val refreshedFvSymbols = dtorBranchFnFvs(branchId._1).map(s => s -> new VarSymbol(Tree.Ident(s"fv_${s.nme}"))) - val bodyWithCorrectSymbols = new RefreshSymbol(refreshedFvSymbols.toMap).applyBlock(actualBody) + val bodyWithCorrectSymbols = refreshExtractedBody(refreshedFvSymbols.toMap, actualBody) FunDefn(N, bms, tSym, branchFunParamFieldSyms(branchId).asParamList :: refreshedFvSymbols.unzip._2.asParamList :: Nil, bodyWithCorrectSymbols @@ -548,7 +576,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): case None => Begin(transformedOgBody, Return(Value.Lit(Tree.UnitLit(true)))) val refreshedFvSymbols = restFnFvs(restFunId).map(s => s -> new VarSymbol(Tree.Ident(s"fv_${s.nme}"))) - val bodyWithCorrectSymbols = new RefreshSymbol(refreshedFvSymbols.toMap).applyBlock(actualBody) + val bodyWithCorrectSymbols = refreshExtractedBody(refreshedFvSymbols.toMap, actualBody) FunDefn(N, bms, tsym, refreshedFvSymbols.unzip._2.asParamList :: Nil, bodyWithCorrectSymbols)(N, annotations = PrivateModifier :: Nil) end newRestFuns 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 e139026c54..e38466ec9a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -666,6 +666,11 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: doc" # switch (${result(scrut)}) { #{ ${bodWithDflt} #} # }" :: returningTerm(rest, endSemi) case Match(scrut, arms @ hd :: tl, els, rest) => val sd = result(scrut) + // * Parenthesize the scrutinee for property access when it's a numeric literal, + // * since things like `12.length` are invalid JS (the `.` is parsed as a decimal point). + def sdProp = scrut match + case Value.Lit(Tree.IntLit(_) | Tree.DecLit(_)) => doc"($sd)" + case _ => sd def cond(cse: Case) = cse match case Case.Lit(lit) => doc"$sd === ${lit.idStr}" case Case.Cls(cls, pth) => cls match @@ -684,7 +689,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: // * ^ Note that modules are currently not valid patterns; // * this case is just for objects, which have their class stored in a `.class` property. case _ => doc"$sd instanceof ${result(pth)}" - case Case.Tup(len, inf) => doc"$runtimeVar.Tuple.isArrayLike($sd) && $sd.length ${if inf then ">=" else "==="} ${len}" + case Case.Tup(len, inf) => doc"$runtimeVar.Tuple.isArrayLike($sd) && $sdProp.length ${if inf then ">=" else "==="} ${len}" case Case.Field(name = n, safe = false) => doc"""typeof $sd === "object" && $sd !== null && "${n.name}" in $sd""" case Case.Field(name = n, safe = true) => diff --git a/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls b/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls index 4911696eb3..3557776d76 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/NonReturningStatements.mls @@ -31,7 +31,7 @@ fun foo = :sjs foo //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ return foo2() +//│ Predef.print(1); Predef.print("..."); return runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 1 //│ > ... diff --git a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls index 94e2f2af21..18ae7a1056 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls @@ -51,17 +51,17 @@ fun sum2 = case //│ sum2 = function sum2() { //│ let lambda; //│ lambda = (undefined, function (caseScrut) { -//│ let lastElement1$3, middleElements3, element0$, tmp5, tmp6, tmp7; +//│ let lastElement1$3, middleElements3, element0$, tmp4, tmp5, tmp6; //│ if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length === 0) { //│ return 0 //│ } else if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length >= 2) { //│ element0$ = runtime.Tuple.get(caseScrut, 0); //│ middleElements3 = runtime.Tuple.slice(caseScrut, 1, 1); //│ lastElement1$3 = runtime.Tuple.get(caseScrut, -1); -//│ tmp5 = element0$ + lastElement1$3; -//│ tmp6 = sum2(); -//│ tmp7 = runtime.safeCall(tmp6(middleElements3)); -//│ return tmp5 + tmp7 +//│ tmp4 = element0$ + lastElement1$3; +//│ tmp5 = sum2(); +//│ tmp6 = runtime.safeCall(tmp5(middleElements3)); +//│ return tmp4 + tmp6 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }); diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls index 5594bcadc3..df156de3fb 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamListClasses.mls @@ -85,7 +85,22 @@ Foo(1, 2) :sjs let f = Foo // should compile to `(x, y) => Foo(x, y)(use[Int])` //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let f, f1; f1 = function f(x, y) { return Foo7(x, y)(instance$Ident$_Int$_) }; f = f1; +//│ let f, f1; +//│ f1 = function f(x1, y1) { +//│ let x2, y2, z2, tmp2, lambda4; +//│ x2 = x1; +//│ y2 = y1; +//│ z2 = instance$Ident$_Int$_; +//│ tmp2 = new Foo03(); +//│ lambda4 = (undefined, function (res) { +//│ res.x = x2; +//│ res.y = y2; +//│ res.z = z2; +//│ return runtime.Unit +//│ }); +//│ return Predef.tap(tmp2, lambda4) +//│ }; +//│ f = f1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f diff --git a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls index 3aa06fd0e9..96f6f53ff4 100644 --- a/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls +++ b/hkmc2/shared/src/test/mlscript/basics/MultiParamLists.mls @@ -51,7 +51,7 @@ fun f(n1: Int)(n2: Int)(n3: Int): Int = 10 * (10 * n1 + n2) + n3 f(4)(2)(0) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ return f$worker(4, 2, 0) +//│ return 420 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 420 @@ -77,7 +77,7 @@ fun f(n1: Int)(n2: Int)(n3: Int)(n4: Int): Int = 10 * (10 * (10 * n1 + n2) + n3) f(3)(0)(3)(1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ return f$worker1(3, 0, 3, 1) +//│ let tmp, tmp1; tmp = 303; tmp1 = 10 * tmp; return tmp1 + 1 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3031 diff --git a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls index d7dd76a158..704b703943 100644 --- a/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/PartialApps.mls @@ -113,19 +113,17 @@ passTo(1, add(., 1) @ _ + _)(2) :sjs let f = add(_, 1) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let f, f1; -//│ f1 = function f(_0) { let tmp1; tmp1 = add(); return runtime.safeCall(tmp1(_0, 1)) }; -//│ f = f1; +//│ let f, f1; f1 = function f(_0) { return _0 + 1 }; f = f1; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ f = fun f :fixme let f = add(., 1) //│ ╔══[PARSE ERROR] Expected an expression; found period instead -//│ ║ l.123: let f = add(., 1) +//│ ║ l.121: let f = add(., 1) //│ ╙── ^ //│ ╔══[PARSE ERROR] Unexpected period here -//│ ║ l.123: let f = add(., 1) +//│ ║ l.121: let f = add(., 1) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. //│ f = undefined diff --git a/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls b/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls index 1506f7c48e..6504910bef 100644 --- a/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls +++ b/hkmc2/shared/src/test/mlscript/basics/ShortcircuitingOps.mls @@ -29,12 +29,7 @@ false && loudTrue :sjs print(1 && loudTrue) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ if (1 === true) { -//│ let inlinedVal; -//│ inlinedVal = loud(true); -//│ return Predef.print(inlinedVal) -//│ } -//│ return Predef.print(false); +//│ if (1 === true) { Predef.print(true); return Predef.print(true) } return Predef.print(false); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > false diff --git a/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls b/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls index a0f59478a1..577e09f113 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/AuxiliaryConstructors.mls @@ -64,6 +64,7 @@ class Baz(a, b) with constructor(x, y)(u, v) fun total() = a + b + x + y + u + v +:expect 21 Baz(1, 2)(3, 4)(5, 6).total() //│ = 21 @@ -90,6 +91,7 @@ class Qux(a) with constructor(extra) fun both() = a + extra +:expect 133 Qux(10)(123).both() //│ = 133 diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls index 7d3491ae64..a63ca02fe7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassInFun.mls @@ -40,23 +40,23 @@ fun test(x) = //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test2; //│ test2 = function test(x) { -//│ let Foo2, tmp; -//│ Foo2 = function Foo(a, b) { -//│ return globalThis.Object.freeze(new Foo.class(a, b)); +//│ let Foo4, tmp; +//│ Foo4 = function Foo(a1, b) { +//│ return globalThis.Object.freeze(new Foo.class(a1, b)); //│ }; -//│ (class Foo1 { +//│ (class Foo3 { //│ static { -//│ Foo2.class = this +//│ Foo4.class = this //│ } -//│ constructor(a, b) { -//│ this.a = a; +//│ constructor(a1, b) { +//│ this.a = a1; //│ this.b = b; //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo", ["a", "b"]]; //│ }); //│ tmp = x + 1; -//│ return Foo2(x, tmp) +//│ return Foo4(x, tmp) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— diff --git a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls index 7fc2363203..4e9b5d1f24 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ClassMatching.mls @@ -153,16 +153,16 @@ fun f(x) = if x is else print("oops") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f4; -//│ f4 = function f(x) { +//│ f4 = function f(x2) { //│ let scrut1, arg$Some$0$1; -//│ if (x instanceof Some1.class) { -//│ arg$Some$0$1 = x.value; +//│ if (x2 instanceof Some1.class) { +//│ arg$Some$0$1 = x2.value; //│ scrut1 = arg$Some$0$1 > 0; //│ if (scrut1 === true) { //│ return 42 //│ } //│ return Predef.print("oops"); -//│ } else if (x instanceof None1.class) { return "ok" } +//│ } else if (x2 instanceof None1.class) { return "ok" } //│ return Predef.print("oops"); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— @@ -189,13 +189,13 @@ fun f(x) = if x is Pair(a, b) then a + b //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f5; -//│ f5 = function f(x) { +//│ f5 = function f(x2) { //│ let arg$Pair$0$, arg$Pair$1$; -//│ if (x instanceof Some1.class) { -//│ return x.value -//│ } else if (x instanceof Pair1.class) { -//│ arg$Pair$0$ = x.fst; -//│ arg$Pair$1$ = x.snd; +//│ if (x2 instanceof Some1.class) { +//│ return x2.value +//│ } else if (x2 instanceof Pair1.class) { +//│ arg$Pair$0$ = x2.fst; +//│ arg$Pair$1$ = x2.snd; //│ return arg$Pair$0$ + arg$Pair$1$ //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); @@ -217,15 +217,17 @@ fun f(x) = print of if x is else "oops" //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; -//│ f6 = function f(x) { +//│ f6 = function f(x2) { //│ let arg$Some$0$1; -//│ if (x instanceof Some1.class) { -//│ arg$Some$0$1 = x.value; +//│ if (x2 instanceof Some1.class) { +//│ arg$Some$0$1 = x2.value; //│ if (arg$Some$0$1 === 0) { //│ return Predef.print("0") //│ } //│ return Predef.print("oops"); -//│ } else if (x instanceof None1.class) { return Predef.print("ok") } +//│ } else if (x2 instanceof None1.class) { +//│ return Predef.print("ok") +//│ } //│ return Predef.print("oops"); //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— diff --git a/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls b/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls index e6b9220364..768ee2af30 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CurriedFunctions.mls @@ -5,7 +5,7 @@ fun foo(x)(y) = z => x + y + z foo(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let foo⁰, foo$worker⁰, inlinedVal; +//│ let foo⁰, foo$worker⁰; //│ @inline //│ define foo⁰ as fun foo¹(x)(y) { //│ return foo$worker¹(x, y) @@ -20,8 +20,7 @@ foo(1)(2)(3) //│ }; //│ return lambda⁰ //│ }; -//│ set inlinedVal = foo$worker¹(1, 2); -//│ return inlinedVal(3) +//│ return 6 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 diff --git a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls index a57c58278f..d2ce3d9baa 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/DelayedLetInit.mls @@ -147,7 +147,7 @@ fun f() = f() //│ —————————————| JS (sanitized) |————————————————————————————————————————————————————————————————————— -//│ block$res17 = runtime.checkCall(f3()); +//│ foo2 = 42; block$res17 = runtime.Unit; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— foo diff --git a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls index 736612015f..b4eeaaa5f4 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FunInClass.mls @@ -76,48 +76,48 @@ fun test(a) = //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test2; //│ test2 = function test(a) { -//│ let C11, C21, tmp, tmp1; -//│ C11 = function C1(b) { +//│ let C12, C22, tmp1, tmp2; +//│ C12 = function C1(b) { //│ return globalThis.Object.freeze(new C1.class(b)); //│ }; -//│ (class C1 { +//│ (class C11 { //│ static { -//│ C11.class = this +//│ C12.class = this //│ } //│ constructor(b) { -//│ let tmp2; +//│ let tmp3; //│ this.b = b; -//│ tmp2 = globalThis.Object.freeze([ +//│ tmp3 = globalThis.Object.freeze([ //│ a, //│ this.b //│ ]); -//│ Predef.print(tmp2); +//│ Predef.print(tmp3); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C1", ["b"]]; //│ }); -//│ C21 = function C2(b) { +//│ C22 = function C2(b) { //│ return globalThis.Object.freeze(new C2.class(b)); //│ }; -//│ (class C2 { +//│ (class C21 { //│ static { -//│ C21.class = this +//│ C22.class = this //│ } //│ constructor(b) { -//│ let tmp2; +//│ let tmp3; //│ this.b = b; -//│ tmp2 = globalThis.Object.freeze([ +//│ tmp3 = globalThis.Object.freeze([ //│ a, //│ this.b //│ ]); -//│ Predef.print(tmp2); +//│ Predef.print(tmp3); //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "C2", ["b"]]; //│ }); -//│ tmp = C11(1); -//│ tmp1 = C21(2); -//│ return globalThis.Object.freeze([ tmp, tmp1 ]) +//│ tmp1 = C12(1); +//│ tmp2 = C22(2); +//│ return globalThis.Object.freeze([ tmp1, tmp2 ]) //│ }; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— diff --git a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls index efc59bf80d..9361b87dc7 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Functions.mls @@ -90,7 +90,7 @@ fun test2(y) = y + 1 //│ REPL> Sending: block$res18 = undefined //│ REPL> Collected: //│ > undefined -//│ REPL> Sending: let test2, test1;try { test1 = function test1(x) { runtime.checkArgs("test1", 1, true, arguments.length); return x + 1 }; test2 = function test2(y) { runtime.checkArgs("test2", 1, true, arguments.length); return y + 1 };; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } +//│ REPL> Sending: let test2, test1;try { test1 = function test1(x2) { runtime.checkArgs("test1", 1, true, arguments.length); return x2 + 1 }; test2 = function test2(y2) { runtime.checkArgs("test2", 1, true, arguments.length); return y2 + 1 };; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: diff --git a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls index 06369d88bb..44e7977a36 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/GlobalThis.mls @@ -44,7 +44,10 @@ foo() //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ }; -//│ return foo() +//│ if (false === true) { +//│ return 0 +//│ } +//│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') //│ ═══[RUNTIME ERROR] TypeError: Cannot read properties of undefined (reading 'freeze') diff --git a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls index 5ac68242ef..65c74b8cf5 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ImportedOps.mls @@ -11,13 +11,14 @@ fun foo() = "a" ~ "b" ~ "c" foo() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let foo; +//│ let foo, tmp; //│ foo = function foo() { -//│ let tmp; -//│ tmp = M1.concat("a", "b"); -//│ return M1.concat(tmp, "c") +//│ let tmp1; +//│ tmp1 = M1.concat("a", "b"); +//│ return M1.concat(tmp1, "c") //│ }; -//│ return foo() +//│ tmp = M1.concat("a", "b"); +//│ return M1.concat(tmp, "c") //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "abc" diff --git a/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls b/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls index c0584f7850..8c386e61bb 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Inliner.mls @@ -348,3 +348,25 @@ fun g() = //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— +// Refresh the constructor symbol together with an inlined local class. +:soir +@inline fun makeFoo() = + class Foo() + Foo() +makeFoo() +//│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— +//│ let makeFoo⁰, Foo⁰; +//│ @inline +//│ define makeFoo⁰ as fun makeFoo¹() { +//│ let Foo; +//│ define Foo as class Foo² { +//│ constructor Foo¹ +//│ }; +//│ return Foo¹() +//│ }; +//│ define Foo⁰ as class Foo⁴ { constructor Foo³ }; +//│ return Foo³() +//│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— +//│ = Foo() + + diff --git a/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls b/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls index 59e3ce4c66..b193758f8b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/MergeMatchArms.mls @@ -154,13 +154,12 @@ if a is let tmp = printAndId(3) B then 2 + tmp //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let tmp1; //│ if (a instanceof A1.class) { //│ return 1 //│ } -//│ tmp1 = printAndId(3); +//│ Predef.print(3); //│ if (a instanceof B1.class) { -//│ return 2 + tmp1 +//│ return 5 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— @@ -204,22 +203,22 @@ if a is print(x + 1) print(x + 2) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let tmp2, tmp3, tmp4; +//│ let tmp1, tmp2, tmp3; //│ if (a instanceof B1.class) { //│ return 1 //│ } //│ if (a instanceof A1.class) { -//│ tmp2 = 2; +//│ tmp1 = 2; //│ } else if (a instanceof C1.class) { -//│ tmp2 = 3; +//│ tmp1 = 3; //│ } else { //│ throw globalThis.Object.freeze(new globalThis.Error("match error")) //│ } +//│ Predef.print(tmp1); +//│ tmp2 = tmp1 + 1; //│ Predef.print(tmp2); -//│ tmp3 = tmp2 + 1; -//│ Predef.print(tmp3); -//│ tmp4 = tmp2 + 2; -//│ return Predef.print(tmp4); +//│ tmp3 = tmp1 + 2; +//│ return Predef.print(tmp3); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > 2 //│ > 3 diff --git a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls index 2c13fa3c45..9cc5bf21ba 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/PlainClasses.mls @@ -100,7 +100,19 @@ fun test() = let t = test() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let t; t = test(); +//│ let t, Foo6; +//│ (class Foo5 { +//│ static { +//│ Foo6 = this +//│ } +//│ constructor() { +//│ Predef.print("hi"); +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["class", "Foo"]; +//│ }); +//│ Predef.print("ok"); +//│ t = Foo6; //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > ok //│ t = class Foo @@ -108,7 +120,7 @@ let t = test() :e new t //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. -//│ ║ l.109: new t +//│ ║ l.121: new t //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— @@ -127,7 +139,7 @@ new! t :e new t() //│ ╔══[COMPILATION ERROR] Expected a statically known class; found reference. -//│ ║ l.128: new t() +//│ ║ l.140: new t() //│ ║ ^ //│ ╙── The 'new' keyword requires a statically known class; use the 'new!' operator for dynamic instantiation. //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— @@ -149,10 +161,10 @@ class Foo with let y = x + 1 fun z() = y + x //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo6; -//│ (class Foo5 { +//│ let Foo8; +//│ (class Foo7 { //│ static { -//│ Foo6 = this +//│ Foo8 = this //│ } //│ constructor() { //│ this.x = 1; @@ -177,10 +189,10 @@ class Foo with fun z2() = 6 print("hello") //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo8; -//│ (class Foo7 { +//│ let Foo10; +//│ (class Foo9 { //│ static { -//│ Foo8 = this +//│ Foo10 = this //│ } //│ constructor() { //│ this.x1 = 1; @@ -204,10 +216,10 @@ class Foo with fun foo(y) = x + y fun bar(z) = foo(z) + 1 //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo10; -//│ (class Foo9 { +//│ let Foo12; +//│ (class Foo11 { //│ static { -//│ Foo10 = this +//│ Foo12 = this //│ } //│ constructor() { //│ this.x = 1; @@ -231,7 +243,7 @@ print(a.foo(1)) print(a.bar(1)) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let a, tmp, tmp1; -//│ a = globalThis.Object.freeze(new Foo10()); +//│ a = globalThis.Object.freeze(new Foo12()); //│ Predef.print(a.x); //│ tmp = runtime.safeCall(a.foo(1)); //│ Predef.print(tmp); @@ -251,16 +263,16 @@ class Foo with val x = 2 //│ ╔══[COMPILATION ERROR] Multiple definitions of symbol 'x' //│ ╟── defined here -//│ ║ l.250: val x = 1 +//│ ║ l.262: val x = 1 //│ ║ ^^^^^^^^^ //│ ╟── defined here -//│ ║ l.251: val x = 2 +//│ ║ l.263: val x = 2 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo12; -//│ (class Foo11 { +//│ let Foo14; +//│ (class Foo13 { //│ static { -//│ Foo12 = this +//│ Foo14 = this //│ } //│ constructor() { //│ this.x = 1; @@ -276,16 +288,16 @@ class Foo with val x = 1 let x = 2 //│ ╔══[COMPILATION ERROR] Name 'x' is already used -//│ ║ l.277: let x = 2 +//│ ║ l.289: let x = 2 //│ ║ ^^^^^ //│ ╟── by a member declared in the same block -//│ ║ l.276: val x = 1 +//│ ║ l.288: val x = 1 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo14; -//│ (class Foo13 { +//│ let Foo16; +//│ (class Foo15 { //│ static { -//│ Foo14 = this +//│ Foo16 = this //│ } //│ constructor() { //│ this.x = 1; @@ -305,13 +317,13 @@ class Foo with :e class Foo with val x = 1 //│ ╔══[COMPILATION ERROR] Illegal body of class definition (should be a block; found term definition). -//│ ║ l.306: class Foo with val x = 1 +//│ ║ l.318: class Foo with val x = 1 //│ ╙── ^^^^^^^^^ //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let Foo16; -//│ (class Foo15 { +//│ let Foo18; +//│ (class Foo17 { //│ static { -//│ Foo16 = this +//│ Foo18 = this //│ } //│ toString() { return runtime.render(this); } //│ static [definitionMetadata] = ["class", "Foo"]; diff --git a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls index 8733eeae0e..6decf80e0a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Throw.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Throw.mls @@ -53,7 +53,10 @@ f(false) //│ } //│ throw runtime.safeCall(globalThis.Error("y")); //│ }; -//│ return f3(false) +//│ if (false === true) { +//│ throw runtime.safeCall(globalThis.Error("x")) +//│ } +//│ throw runtime.safeCall(globalThis.Error("y")); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ ═══[RUNTIME ERROR] Error: y diff --git a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls index caa30e878a..0f281880a1 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/TraceLogIndent.mls @@ -67,7 +67,7 @@ f(1,2)(-3) //│ REPL> Sending: block$res9 = undefined //│ REPL> Collected: //│ > undefined -//│ REPL> Sending: try { block$res9 = runtime.checkCall(f$worker(1, 2, -3));; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } +//│ REPL> Sending: let tmp2;try { tmp2 = runtime.checkCall(g(-3)); block$res9 = 3 + tmp2;; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > CALL g(-3) //│ > | CALL g(-2) @@ -107,7 +107,7 @@ f(1,2)(-4) //│ REPL> Sending: block$res10 = undefined //│ REPL> Collected: //│ > undefined -//│ REPL> Sending: try { block$res10 = runtime.checkCall(f$worker(1, 2, -4));; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } +//│ REPL> Sending: let tmp3;try { tmp3 = runtime.checkCall(g(-4)); block$res10 = 3 + tmp3;; undefined } catch (e) { console.log('\u200B' + (e.stack ?? e) + '\u200B'); } //│ REPL> Collected: //│ > undefined //│ REPL> Parsed: diff --git a/hkmc2/shared/src/test/mlscript/codegen/While.mls b/hkmc2/shared/src/test/mlscript/codegen/While.mls index af1d66bd9d..ec52b48ca8 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/While.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/While.mls @@ -45,10 +45,10 @@ fun f = while x then print("Hello World") set x = false - else return 42 // TODO: support `break` + else return 42 // TODO: support `break` here f //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let f; +//│ let f, inlinedVal; //│ f = function f() { //│ lbl4: while (true) { //│ if (x2 === true) { @@ -59,7 +59,18 @@ f //│ return 42; //│ } //│ }; -//│ return f() +//│ inlinedLbl: { +//│ lbl4: while (true) { +//│ if (x2 === true) { +//│ Predef.print("Hello World"); +//│ x2 = false; +//│ continue lbl4 +//│ } +//│ inlinedVal = 42; +//│ break inlinedLbl; +//│ } +//│ } +//│ return inlinedVal //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hello World //│ = 42 @@ -190,14 +201,14 @@ fun f(ls) = //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let f6; //│ f6 = function f(ls) { -//│ lbl6: while (true) { +//│ lbl9: while (true) { //│ let arg$Cons$0$, arg$Cons$1$; //│ if (ls instanceof Cons1.class) { //│ arg$Cons$0$ = ls.hd; //│ arg$Cons$1$ = ls.tl; //│ ls = arg$Cons$1$; //│ Predef.print(arg$Cons$0$); -//│ continue lbl6 +//│ continue lbl9 //│ } //│ break; //│ } @@ -240,7 +251,7 @@ let x = 1 :sjs while x is {} do() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ lbl7: while (true) { if (x3 instanceof Object) { continue lbl7 } break; } return runtime.Unit +//│ lbl11: while (true) { if (x3 instanceof Object) { continue lbl11 } break; } return runtime.Unit //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— @@ -312,10 +323,10 @@ while print("Hello World"); false then 0(0) else 1 //│ ╔══[PARSE ERROR] Unexpected 'then' keyword here -//│ ║ l.312: then 0(0) +//│ ║ l.323: then 0(0) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized term split (false literal) -//│ ║ l.311: while print("Hello World"); false +//│ ║ l.322: while print("Hello World"); false //│ ╙── ^^^^^ //│ > Hello World @@ -324,12 +335,12 @@ while { print("Hello World"), false } then 0(0) else 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//│ ║ l.323: while { print("Hello World"), false } +//│ ║ l.334: while { print("Hello World"), false } //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.324: then 0(0) +//│ ║ l.335: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. -//│ ║ l.325: else 1 +//│ ║ l.336: else 1 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -340,14 +351,14 @@ while then 0(0) else 1 //│ ╔══[COMPILATION ERROR] Unexpected infix use of keyword 'then' here -//│ ║ l.338: print("Hello World") +//│ ║ l.349: print("Hello World") //│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.339: false +//│ ║ l.350: false //│ ║ ^^^^^^^^^ -//│ ║ l.340: then 0(0) +//│ ║ l.351: then 0(0) //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Illegal position for prefix keyword 'else'. -//│ ║ l.341: else 1 +//│ ║ l.352: else 1 //│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls index cfe5757c67..443f5c7d07 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/MissingDefinitions1.mls @@ -29,13 +29,13 @@ fun g(x) = f(x) :ge :re g(0) -//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'g' -//│ ║ l.31: g(0) -//│ ║ ^ -//│ ╟── which references the symbol introduced here +//│ ╔══[COMPILATION ERROR] No definition found in scope for member 'f' //│ ║ l.26: fun g(x) = f(x) -//│ ╙── ^^^^^^^^^^^^^^^ -//│ ═══[RUNTIME ERROR] ReferenceError: g is not defined +//│ ║ ^ +//│ ╟── which references the symbol introduced here +//│ ║ l.3: fun f: Any -> Any +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ ═══[RUNTIME ERROR] ReferenceError: f is not defined :js :ge diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls index 67808baada..f10cd64c2c 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/lambda.mls @@ -45,6 +45,7 @@ let g = (b) => 0 private fun pick(x)(y) = if false then x else y pick(f)(g)(5) //│ dead-param-elim > >>> dead-param-elim results >>> +//│ dead-param-elim > prodfun pick$worker#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 //│ g = fun g @@ -55,6 +56,7 @@ let g = (b) => 0 private fun pick(x)(y) = if false then x else y pick(f)(g)(5) //│ dead-param-elim > >>> dead-param-elim results >>> +//│ dead-param-elim > prodfun pick$worker#0 @ -> eliminable: {0, 1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 //│ g = fun g @@ -88,6 +90,8 @@ main1() //│ = 1 +// [INLINING PR FIXME]: regression? even with :noInline +:noInline // two `apply`, one has dead param, another does not; // but if `apply` is inlined, we can eliminate the `a` in `(a) => 0` private fun apply(f, x) = f(x) @@ -96,7 +100,6 @@ private fun main(f1, f2) = private fun main1() = main((a) => 0, (a) => a + 1) main1() //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun lambda#0 @ -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 @@ -117,6 +120,7 @@ main1() :deadParamElim debug mono :expect 0 +:noInline private fun f = (a) => 0 f(3) //│ dead-param-elim > >>> dead-param-elim results >>> diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls index 8f9a5bbfa9..6142d9382e 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/module.mls @@ -1,5 +1,6 @@ :js :deadParamElim debug +:noInline diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls index 469325e533..86d37f8a3f 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/recursive.mls @@ -1,5 +1,6 @@ :js :deadParamElim debug +:noInline :expect 0 @@ -153,23 +154,18 @@ pong(5, 0) //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 0 +// [INLINING PR FIXME]: regression? no worker/wrapper anymore -> should have an option to only disable inlining of big functions and keep worker/wrapper! :soir :noTailRec private fun ping(n)(unused) = if n == 0 then 0 else pong(n - 1)(unused) private fun pong(n)(unused) = if n == 0 then 0 else ping(n - 1)(unused) ping(5)(99) //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun ping$worker#0 @ -> eliminable: {1} -//│ dead-param-elim > prodfun pong$worker#0 @ -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let ping⁴, pong⁴, pong$worker⁰, ping$worker⁰; -//│ @inline @private -//│ define ping⁴ as fun ping⁵(n)(unused) { -//│ return ping$worker¹(n) -//│ }; +//│ let ping⁴, pong⁴; //│ @private -//│ define ping$worker⁰ as fun ping$worker¹(n) { +//│ define ping⁴ as fun ping⁵(n)(unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut @@ -177,15 +173,11 @@ ping(5)(99) //│ return 0 //│ else //│ set tmp = -⁰(n, 1); -//│ return pong$worker¹(tmp) +//│ return pong⁵(tmp)(unused) //│ end //│ }; -//│ @inline @private -//│ define pong⁴ as fun pong⁵(n)(unused) { -//│ return pong$worker¹(n) -//│ }; //│ @private -//│ define pong$worker⁰ as fun pong$worker¹(n) { +//│ define pong⁴ as fun pong⁵(n)(unused) { //│ let scrut, tmp; //│ set scrut = Predef⁰.equals⁰(n, 0); //│ match scrut @@ -193,10 +185,10 @@ ping(5)(99) //│ return 0 //│ else //│ set tmp = -⁰(n, 1); -//│ return ping$worker¹(tmp) +//│ return ping⁵(tmp)(unused) //│ end //│ }; -//│ return ping$worker¹(5) +//│ return ping⁵(5)(99) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls b/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls index 1396702c13..fabe184a5f 100644 --- a/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls +++ b/hkmc2/shared/src/test/mlscript/dead-param-elim/refresher.mls @@ -10,7 +10,6 @@ fun f(used, unused) = new C f(1, 2).get() //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun f#0 @ f@1 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 1 @@ -23,7 +22,6 @@ fun foo(y) = (new M).bar() foo(10) //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun foo#0 @ foo@3 -> eliminable: {0} //│ dead-param-elim > <<< dead-param-elim results <<< @@ -38,7 +36,6 @@ fun g(x, dead) = p.first() g(10, 99) //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun g#0 @ g@3 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 10 @@ -52,7 +49,6 @@ fun h(base, unused) = new Adder h(10, 0).add(3) //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun h#0 @ h@2 -> eliminable: {1} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 13 @@ -122,6 +118,5 @@ fun multi(a, b, dead) = (new Calc).prod() multi(5, 6, 999) //│ dead-param-elim > >>> dead-param-elim results >>> -//│ dead-param-elim > prodfun multi#0 @ multi@4 -> eliminable: {2} //│ dead-param-elim > <<< dead-param-elim results <<< //│ = 30 diff --git a/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls b/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls index 6d5d2df582..cd413278c5 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/fusibility.mls @@ -97,7 +97,7 @@ fun c(x) = if x is AA(_) then 0 let p = AA(10) c(p) + c(p) //│ deforest > >>> non-affine syms >>> -//│ deforest > p@3 +//│ deforest > p@1 //│ deforest > <<< non-affine syms <<< //│ deforest > >>> fusing >>> //│ deforest > <<< fusing <<< diff --git a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls index 9aeff80d9c..1a207def8e 100644 --- a/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls +++ b/hkmc2/shared/src/test/mlscript/deforest/listComprehension.mls @@ -247,6 +247,8 @@ if A is //│ = Cons(2, Cons(3, Cons(4, Nil))) +// TODO: fix lifter +:fixme :lift :deforest fun h() = @@ -289,6 +291,50 @@ h() //│ deforest > Nil⁰ -> //│ deforest > match: ls² //│ deforest > <<< fusing <<< +//│ /!!!\ Uncaught error: java.lang.Exception: Internal Error: Cannot resolve overloaded member symbol lscomp2: no principal disambiguation found + +// Same but without lifting: +:deforest +fun h() = + fun lscomp1(ls) = if ls is + Nil then Nil + x :: xs then + fun lscomp2(ls) = if ls is + Nil then lscomp1(xs) + y :: ys then [x, y] :: lscomp2(ys) + lscomp2(1 :: 2 :: 3 :: Nil) + lscomp1(1 :: 2 :: 3 :: Nil) +h() +//│ deforest > >>> fusing >>> +//│ deforest > Cons⁰(1, tmp¹⁶) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Cons⁰(2, tmp¹⁷) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Cons⁰(3, Nil⁰) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Nil⁰ -> +//│ deforest > match: ls³ +//│ deforest > Cons⁰(1, tmp¹⁶) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Cons⁰(2, tmp¹⁷) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Cons⁰(3, Nil⁰) -> +//│ deforest > match: ls³ +//│ deforest > fields: ls³.h⁰ +//│ deforest > fields: ls³.t⁰ +//│ deforest > Nil⁰ -> +//│ deforest > match: ls³ +//│ deforest > <<< fusing <<< //│ = Cons( //│ [1, 1], //│ Cons( diff --git a/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls b/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls index ad5acd3d0a..f5a3a22735 100644 --- a/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/invalml/InvalMLCodeGen.mls @@ -203,7 +203,12 @@ fun nott = case :sjs nott of false //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let tmp; tmp = nott(); return runtime.safeCall(tmp(false)) +//│ if (false === true) { +//│ return false +//│ } else if (false === false) { +//│ return true +//│ } +//│ throw globalThis.Object.freeze(new globalThis.Error.class("match error")); //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true //│ Type: Bool @@ -218,14 +223,14 @@ fun fact = case //│ fact = function fact() { //│ let lambda2; //│ lambda2 = (undefined, function (caseScrut) { -//│ let tmp1, tmp2, tmp3; +//│ let tmp, tmp1, tmp2; //│ if (caseScrut === 0) { //│ return 1 //│ } -//│ tmp1 = fact(); -//│ tmp2 = caseScrut - 1; -//│ tmp3 = runtime.safeCall(tmp1(tmp2)); -//│ return caseScrut * tmp3; +//│ tmp = fact(); +//│ tmp1 = caseScrut - 1; +//│ tmp2 = runtime.safeCall(tmp(tmp1)); +//│ return caseScrut * tmp2; //│ }); //│ return lambda2 //│ }; diff --git a/hkmc2/shared/src/test/mlscript/invalml/InvalMLGetters.mls b/hkmc2/shared/src/test/mlscript/invalml/InvalMLGetters.mls index 240264e26d..f4e6bd2daa 100644 --- a/hkmc2/shared/src/test/mlscript/invalml/InvalMLGetters.mls +++ b/hkmc2/shared/src/test/mlscript/invalml/InvalMLGetters.mls @@ -90,19 +90,79 @@ fun test2() = //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let test22; //│ test22 = function test2() { -//│ let funny; +//│ let funny, lambda1; //│ funny = function funny() { -//│ let lambda1; -//│ lambda1 = (undefined, function (caseScrut) { +//│ let lambda2; +//│ lambda2 = (undefined, function (caseScrut) { //│ let tmp, tmp1, tmp2; //│ tmp = funny(); //│ tmp1 = caseScrut - 1; //│ tmp2 = runtime.safeCall(tmp(tmp1)); //│ return tmp2 + 1 //│ }); -//│ return lambda1 +//│ return lambda2 //│ }; -//│ return funny() +//│ lambda1 = (undefined, function (caseScrut) { +//│ let tmp, inlinedVal, tmp1, inlinedVal1, tmp2, inlinedVal2, tmp3, inlinedVal3, tmp4, tmp5, caseScrut1, inlinedVal4; +//│ tmp = caseScrut - 1; +//│ tmp1 = tmp - 1; +//│ tmp2 = tmp1 - 1; +//│ tmp3 = tmp2 - 1; +//│ tmp4 = tmp3 - 1; +//│ caseScrut1 = tmp4; +//│ inlinedLbl: { +//│ let tmp6, tmp7, caseScrut2, inlinedVal5; +//│ tmp6 = tmp4 - 1; +//│ caseScrut2 = tmp6; +//│ inlinedLbl1: { +//│ let tmp8, tmp9, inlinedVal6, lambda2; +//│ lambda2 = (undefined, function (caseScrut3) { +//│ let tmp10, tmp11, tmp12, inlinedVal7; +//│ inlinedLbl2: { +//│ let lambda3; +//│ lambda3 = (undefined, function (caseScrut4) { +//│ let tmp13, tmp14, tmp15, inlinedVal8; +//│ inlinedLbl3: { +//│ let lambda4; +//│ lambda4 = (undefined, function (caseScrut5) { +//│ let tmp16, tmp17, tmp18; +//│ tmp16 = funny(); +//│ tmp17 = caseScrut5 - 1; +//│ tmp18 = runtime.safeCall(tmp16(tmp17)); +//│ return tmp18 + 1 +//│ }); +//│ inlinedVal8 = lambda4; +//│ break inlinedLbl3; +//│ } +//│ tmp13 = inlinedVal8; +//│ tmp14 = caseScrut4 - 1; +//│ tmp15 = runtime.safeCall(tmp13(tmp14)); +//│ return tmp15 + 1 +//│ }); +//│ inlinedVal7 = lambda3; +//│ } +//│ tmp10 = inlinedVal7; +//│ tmp11 = caseScrut3 - 1; +//│ tmp12 = runtime.safeCall(inlinedVal7(tmp11)); +//│ return tmp12 + 1 +//│ }); +//│ inlinedVal6 = lambda2; +//│ tmp8 = caseScrut2 - 1; +//│ tmp9 = runtime.safeCall(lambda2(tmp8)); +//│ inlinedVal5 = tmp9 + 1; +//│ break inlinedLbl1; +//│ } +//│ tmp7 = inlinedVal5; +//│ inlinedVal4 = tmp7 + 1; +//│ } +//│ tmp5 = inlinedVal4; +//│ inlinedVal3 = inlinedVal4 + 1; +//│ inlinedVal2 = inlinedVal3 + 1; +//│ inlinedVal1 = inlinedVal2 + 1; +//│ inlinedVal = inlinedVal1 + 1; +//│ return inlinedVal + 1 +//│ }); +//│ return lambda1 //│ }; //│ ╔══[WARNING] Pure expression in statement position //│ ║ l.84: case 0 then 0 @@ -122,7 +182,7 @@ fun test2() = fun test3 = print("Hi") //│ ╔══[COMPILATION ERROR] Function definition shape not yet supported for test3 -//│ ║ l.123: print("Hi") +//│ ║ l.183: print("Hi") //│ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls index ab0859ecb4..7a78953375 100644 --- a/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls +++ b/hkmc2/shared/src/test/mlscript/meta/ImporterTest.mls @@ -8,7 +8,7 @@ :sjs hello() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ return hello() +//│ return "Hello!" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "Hello!" @@ -18,7 +18,7 @@ fun hello() = "Hello?" :sjs hello() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ return hello1() +//│ return "Hello?" //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = "Hello?" diff --git a/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls b/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls index 594528159b..348bf99df3 100644 --- a/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls +++ b/hkmc2/shared/src/test/mlscript/opt/BasicVarPropag.mls @@ -436,14 +436,18 @@ fun main = test main //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let main⁸; +//│ let main⁸, lambda⁰; //│ define main⁸ as fun main⁹() { //│ let lambda; //│ @private -//│ define lambda as fun lambda⁰(x) { return x }; -//│ return lambda⁰ +//│ define lambda as fun lambda¹(x) { +//│ return x +//│ }; +//│ return lambda¹ //│ }; -//│ return main⁹() +//│ @private +//│ define lambda⁰ as fun lambda²(x) { return x }; +//│ return lambda² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun @@ -453,14 +457,18 @@ fun main = test main //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let main¹⁰; +//│ let main¹⁰, test⁰; //│ define main¹⁰ as fun main¹¹() { //│ let test; //│ @private -//│ define test as fun test⁰(x) { return x }; -//│ return test⁰ +//│ define test as fun test¹(x) { +//│ return x +//│ }; +//│ return test¹ //│ }; -//│ return main¹¹() +//│ @private +//│ define test⁰ as fun test²(x) { return x }; +//│ return test² //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = fun test diff --git a/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls b/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls index 3b0764c51e..9f0e53e550 100644 --- a/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls +++ b/hkmc2/shared/src/test/mlscript/opt/DeadObjRemoval.mls @@ -24,15 +24,15 @@ fun f() = 42 f() //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let f1; +//│ let f1, A2; //│ f1 = function f() { -//│ let A1; +//│ let A3; //│ (class A { //│ static { //│ new this //│ } //│ constructor() { -//│ A1 = this; +//│ A3 = this; //│ Predef.print("Hello"); //│ Object.defineProperty(this, "class", { //│ value: A @@ -44,17 +44,36 @@ f() //│ }); //│ return 42 //│ }; -//│ return f1() +//│ (class A1 { +//│ static { +//│ new this +//│ } +//│ constructor() { +//│ A2 = this; +//│ Predef.print("Hello"); +//│ Object.defineProperty(this, "class", { +//│ value: A1 +//│ }); +//│ globalThis.Object.freeze(this); +//│ } +//│ toString() { return runtime.render(this); } +//│ static [definitionMetadata] = ["object", "A"]; +//│ }); +//│ return 42 //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let f²; +//│ let f², A⁰; //│ define f² as fun f³() { //│ let A; -//│ define A as object A⁰ { -//│ constructor() { do Predef⁰.print⁰("Hello"); end } +//│ define A as object A¹ { +//│ constructor() { +//│ do Predef⁰.print⁰("Hello"); +//│ end +//│ } //│ }; //│ return 42 //│ }; -//│ return f³() +//│ define A⁰ as object A² { constructor() { do Predef⁰.print⁰("Hello"); end } }; +//│ return 42 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > Hello //│ = 42 diff --git a/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls b/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls index 010bb513a7..bef91c58c5 100644 --- a/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls +++ b/hkmc2/shared/src/test/mlscript/opt/PureCallPropagation.mls @@ -18,7 +18,7 @@ fun foo(x)(y)(z) = :soir foo(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ return foo$worker⁰(1, 2, 3) +//│ do Predef⁰.print⁰("hi"); return 6 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = 6 @@ -30,12 +30,12 @@ let f = foo(1)(2) print("hello") f(3) + f(4) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let f⁰, inlinedVal, inlinedVal1; +//│ let f⁰; //│ set f⁰ = foo⁰(1)(2); //│ do Predef⁰.print⁰("hello"); -//│ set inlinedVal = foo$worker⁰(1, 2, 3); -//│ set inlinedVal1 = foo$worker⁰(1, 2, 4); -//│ return +⁰(inlinedVal, inlinedVal1) +//│ do Predef⁰.print⁰("hi"); +//│ do Predef⁰.print⁰("hi"); +//│ return 13 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hello //│ > hi @@ -81,12 +81,12 @@ let f = foo(1)(2) print("hello") f(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let foo¹, f², foo$worker¹, lambda⁰; +//│ let foo¹, f², foo$worker⁰, lambda⁰; //│ @inline //│ define foo¹ as fun foo²(x)(y) { -//│ return foo$worker²(x, y) +//│ return foo$worker¹(x, y) //│ }; -//│ define foo$worker¹ as fun foo$worker²(x, y) { +//│ define foo$worker⁰ as fun foo$worker¹(x, y) { //│ let lambda; //│ do Predef⁰.print⁰("hi"); //│ @private @@ -115,7 +115,7 @@ f(3) :soir foo(1)(2)(3) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— -//│ let inlinedVal; set inlinedVal = foo$worker²(1, 2); return inlinedVal(3) +//│ let inlinedVal; set inlinedVal = foo$worker¹(1, 2); return inlinedVal(3) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ > hi //│ = 42 @@ -127,7 +127,7 @@ print("hello") f(3) + f(4) //│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— //│ let f³, tmp, tmp1, inlinedVal; -//│ set inlinedVal = foo$worker²(1, 2); +//│ set inlinedVal = foo$worker¹(1, 2); //│ set f³ = inlinedVal; //│ do Predef⁰.print⁰("hello"); //│ set tmp = inlinedVal(3); diff --git a/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls b/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls index 2babce022b..3612946019 100644 --- a/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls +++ b/hkmc2/shared/src/test/mlscript/opt/WorkerWrapper.mls @@ -25,7 +25,7 @@ foo(1)(2) //│ set tmp1 = +⁰(tmp, x); //│ return +⁰(tmp1, y) //│ }; -//│ return foo$worker¹(1, 2) +//│ return 6 //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 6 diff --git a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls index f02dc3c6f0..2b37824c51 100644 --- a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls @@ -136,19 +136,19 @@ fun popByIndex(start, end, acc, lft) = //│ let popByIndex; //│ popByIndex = function popByIndex(start, end, acc, lft) { //│ loopLabel: while (true) { -//│ let scrut, tmp33, tmp34, tmp35; -//│ scrut = start >= end; -//│ if (scrut === true) { +//│ let scrut1, tmp32, tmp33, tmp34; +//│ scrut1 = start >= end; +//│ if (scrut1 === true) { //│ return acc //│ } -//│ tmp33 = start + 1; -//│ tmp34 = runtime.safeCall(lft.at(start)); -//│ tmp35 = globalThis.Object.freeze([ +//│ tmp32 = start + 1; +//│ tmp33 = runtime.safeCall(lft.at(start)); +//│ tmp34 = globalThis.Object.freeze([ //│ ...acc, -//│ tmp34 +//│ tmp33 //│ ]); -//│ start = tmp33; -//│ acc = tmp35; +//│ start = tmp32; +//│ acc = tmp34; //│ continue loopLabel; //│ } //│ }; diff --git a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls index 7f9daa9e64..9d57763640 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/general/LogicalConnectives.mls @@ -25,10 +25,9 @@ fun test(x) = :sjs true and test(42) //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— -//│ let scrut1; //│ if (true === true) { -//│ scrut1 = test(42); -//│ if (scrut1 === true) { return true } +//│ Predef.print(42); +//│ if (42 === true) { return true } //│ return false; //│ } //│ return false; @@ -39,7 +38,7 @@ true and test(42) :fixme true or test(42) //│ ╔══[COMPILATION ERROR] Logical `or` is not yet supported. -//│ ║ l.40: true or test(42) +//│ ║ l.39: true or test(42) //│ ╙── ^^^^^^^^^^^^^^^^ //│ > 42 //│ = false diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls index c6e7cf93aa..9f2e8b0aed 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls @@ -61,17 +61,17 @@ fun nested_tuple_patterns(xs) = if xs is //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let nested_tuple_patterns; //│ nested_tuple_patterns = function nested_tuple_patterns(xs) { -//│ let lastElement1$, middleElements, element0$, element1$, element0$1, tmp6, tmp7; +//│ let lastElement1$4, middleElements, element0$4, element1$, element0$5, tmp6, tmp7; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { -//│ element0$ = runtime.Tuple.get(xs, 0); +//│ element0$4 = runtime.Tuple.get(xs, 0); //│ middleElements = runtime.Tuple.slice(xs, 1, 1); -//│ lastElement1$ = runtime.Tuple.get(xs, -1); +//│ lastElement1$4 = runtime.Tuple.get(xs, -1); //│ if (runtime.Tuple.isArrayLike(middleElements) && middleElements.length === 2) { -//│ element0$1 = runtime.Tuple.get(middleElements, 0); +//│ element0$5 = runtime.Tuple.get(middleElements, 0); //│ element1$ = runtime.Tuple.get(middleElements, 1); -//│ tmp6 = element0$ + element0$1; +//│ tmp6 = element0$4 + element0$5; //│ tmp7 = tmp6 + element1$; -//│ return tmp7 + lastElement1$ +//│ return tmp7 + lastElement1$4 //│ } //│ throw globalThis.Object.freeze(new globalThis.Error("match error")); //│ } else if (runtime.Tuple.isArrayLike(xs) && xs.length === 0) { diff --git a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls index 1fc45c466a..7bebfccff5 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ControlFlow.mls @@ -229,12 +229,43 @@ tailMatchAllValue(1) //│ (local.get $matchRes))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $tailMatchAllValue (ref null any)) +//│ (local $matchRes (ref null any)) //│ (block (result (ref null any)) //│ (nop) -//│ (return -//│ (call $tailMatchAllValue -//│ (ref.i31 -//│ (i32.const 1)))))) +//│ (block (result (ref null any)) +//│ (block $match +//│ (local.set $matchRes +//│ (ref.null any)) +//│ (if +//│ (i32.eq +//│ (i31.get_s +//│ (ref.cast (ref null i31) +//│ (ref.i31 +//│ (i32.const 1)))) +//│ (i32.const 0)) +//│ (then +//│ (block $arm +//│ (return +//│ (ref.i31 +//│ (i32.const 10))) +//│ (br $match)))) +//│ (if +//│ (i32.eq +//│ (i31.get_s +//│ (ref.cast (ref null i31) +//│ (ref.i31 +//│ (i32.const 1)))) +//│ (i32.const 1)) +//│ (then +//│ (block $arm +//│ (return +//│ (ref.i31 +//│ (i32.const 20))) +//│ (br $match)))) +//│ (return +//│ (ref.i31 +//│ (i32.const 30)))) +//│ (local.get $matchRes)))) //│ (elem $tailMatchAllValue declare func $tailMatchAllValue) //│ (elem $entry declare func $entry)) //│ Wasm result: diff --git a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls index cc19959348..836cd0d4f5 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ScopedLocals.mls @@ -71,12 +71,27 @@ chain(1) //│ (local.get $b)))))) //│ (func $entry (export "entry") (type $entry) (result (ref null any)) //│ (local $chain (ref null any)) +//│ (local $a (ref null any)) +//│ (local $b (ref null any)) //│ (block (result (ref null any)) //│ (nop) -//│ (return -//│ (call $chain -//│ (ref.i31 -//│ (i32.const 1)))))) +//│ (block (result (ref null any)) +//│ (local.set $a +//│ (call $plus_impl +//│ (ref.i31 +//│ (i32.const 1)) +//│ (ref.i31 +//│ (i32.const 1)))) +//│ (block (result (ref null any)) +//│ (local.set $b +//│ (call $plus_impl +//│ (local.get $a) +//│ (ref.i31 +//│ (i32.const 1)))) +//│ (return +//│ (call $plus_impl +//│ (local.get $a) +//│ (local.get $b))))))) //│ (elem $chain declare func $chain) //│ (elem $entry declare func $entry)) //│ Wasm result: diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala index cd3d948f97..7a3b9fdf07 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/MLsDiffMaker.scala @@ -179,7 +179,8 @@ abstract class MLsDiffMaker extends DiffMaker: reportExclusiveFlagConflict(":etaExpansion", etaExpansionFlags, "on", "off") if etaExpansionFlags.contains("off") then N else S(EtaExpansion.withDebug(etaExpansionFlags.contains("debug"))), - inlining = Opt.when(!noInlineOpt.isSet)(Config.Inliner(inlineThreshold.get.getOrElse(1))), + inlining = Opt.when(!noInlineOpt.isSet)(Config.Inliner(inlineThreshold = + inlineThreshold.get.getOrElse(Config.default.inlineThreshold))), deadBranchRemoval = Config.default.deadBranchRemoval, qqEnabled = importQQ.isSet, funcToCls = funcToCls.isSet,