From b8163b81e7e156c19b1d90141f8e9d0cfe244440 Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 29 Sep 2026 22:02:42 +0800 Subject: [PATCH 1/5] Preserve explicit source locations on semantic declarations --- .../src/main/scala/hkmc2/AsyncLowering.scala | 2 +- .../src/main/scala/hkmc2/codegen/Block.scala | 2 +- .../hkmc2/codegen/BlockTransformer.scala | 2 +- .../hkmc2/codegen/BufferableTransform.scala | 4 +- .../codegen/FlowAnalysisBasedRewrite.scala | 2 +- .../src/main/scala/hkmc2/codegen/Lifter.scala | 6 +- .../main/scala/hkmc2/codegen/Lowering.scala | 20 ++-- .../scala/hkmc2/codegen/WorkerWrapper.scala | 2 +- .../hkmc2/codegen/deforest/Rewrite.scala | 2 +- .../scala/hkmc2/semantics/Elaborator.scala | 44 ++++----- .../src/main/scala/hkmc2/semantics/Term.scala | 53 ++++++----- .../hkmc2/semantics/ucs/TermSynthesizer.scala | 2 +- .../hkmc2/semantics/ups/SplitCompiler.scala | 10 +- .../test/mlscript/basics/BadTypeClasses.mls | 4 +- .../test/mlscript/codegen/CurriedClasses.mls | 4 +- .../test/mlscript/codegen/ParamClasses.mls | 4 +- .../mlscript/codegen/ThisCallVariations.mls | 2 +- .../mlscript/ctx/CascadingTypeClasses.mls | 12 +-- .../src/test/mlscript/ctx/ClassCtxParams.mls | 8 +- .../src/test/mlscript/ctx/EtaExpansion.mls | 2 +- .../src/test/mlscript/ctx/ExplicitlySpec.mls | 10 +- .../mlscript/ctx/ForwardTypeClassUses.mls | 4 +- hkmc2/shared/src/test/mlscript/ctx/Summon.mls | 8 +- .../src/test/mlscript/lifter/ClassInFun.mls | 2 +- .../mlscript/newres/DeclarationLocations.mls | 95 +++++++++++++++++++ .../src/test/mlscript/objbuf/BasicsObjBuf.mls | 22 ++--- .../src/test/mlscript/parser/Handler.mls | 16 ++-- 27 files changed, 221 insertions(+), 123 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala index d055686337..fcee534a8e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala @@ -39,7 +39,7 @@ class AsyncLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx, Config): pl.allParams.map: p => val v = p.sym val nv = VarSymbol(v.id, erasedType = v.erasedType) - (p, p.copy(sym = nv)) + (p, p.copy(sym = nv)(p.toLoc)) val symMap = outerParams.iterator.map(p => p._1.sym -> p._2.sym).toMap[SimpleSymbol, SimpleSymbol] val thisVar = VarSymbol(Tree.Ident("this"), erasedType = fun.owner.flatMap(_.asThis.erasedValueType)) val thisParam = fun.owner.map(_ => Param.simple(thisVar)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 2ba9103b0e..1c099ed4d9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -582,7 +582,7 @@ object HandleBlock: val handlerMtds = handlers.map: handler => val sym = BlockMemberSymbol(cls.nme + handler.sym.nme, Nil, true) val fDef = FunDefn.withFreshSymbol( - N, sym, PlainParamList(Param(FldFlags.empty, handler.resumeSym, N, Modulefulness.none) :: Nil) :: Nil, + N, sym, PlainParamList(Param.simple(handler.resumeSym) :: Nil) :: Nil, handler.body )(N, annotations = Nil) val rSym = TempSymbol(N, erasedType = N, "suspendRes") diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index c3a45db9bf..8f3588070a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -300,7 +300,7 @@ class BlockTransformer(subst: SymbolSubst): def applyParamList(pl: ParamList): ParamList = def applyParam(p: Param): Param = val sym2 = p.sym.subst - if sym2 is p.sym then p else p.copy(sym = sym2) + if sym2 is p.sym then p else p.copy(sym = sym2)(p.toLoc) val params2 = pl.params.mapConserve(applyParam) val rest2 = pl.restParam.mapConserve(applyParam) if (params2 is pl.params) && (rest2 is pl.restParam) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala index 863777278e..aa9b81d710 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala @@ -34,7 +34,7 @@ class BufferableTransform()(using State, Raise): (sym, VarSymbol(sym.id, erasedType = N)) .toMap def mapParam(p: Param) = - Param(p.flags, varMap(p.sym), p.sign, p.modulefulness) + Param(p.flags, varMap(p.sym), p.sign, p.modulefulness)(p.toLoc) (params.map(pl => ParamList(pl.flags, pl.params.map(mapParam), pl.restParam.map(mapParam))), varMap.toMap) def mkFieldReplacer(buf: VarSymbol, baseIdx: VarSymbol, symMap: Map[SimpleSymbol, SimpleSymbol]) = def getOffset(off: Int)(k: Path => Block): Block = @@ -79,7 +79,7 @@ class BufferableTransform()(using State, Raise): val (newParams, symMap) = mkSymbolReplacer(f.params) val blk = mkFieldReplacer(buf, idx, symMap).applyBlock(f.body) FunDefn(f.owner, f.sym, TermSymbol(f.dSym.k, f.dSym.owner, f.dSym.id, erasedType = N), PlainParamList( - Param(FldFlags.empty, buf, N, Modulefulness.none) :: Param(FldFlags.empty, idx, N, Modulefulness.none) :: Nil) :: newParams, + Param.simple(buf) :: Param.simple(idx) :: Nil) :: newParams, if isCtor then Begin(blk, Return(idx.asSimpleRef)) else blk)(configOverride = f.configOverride, annotations = f.annotations) val fakeCtor = transformFunDefn(FunDefn.withFreshSymbol( S(companionSym), diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala index a6d86e0b4a..958d35d332 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala @@ -262,7 +262,7 @@ class FlowAnalysisBasedRewrite( def refreshParam(p: Param): Param = val newSym = new VarSymbol(Tree.Ident(p.sym.name), erasedType = p.sym.erasedType) refreshParamMap(p.sym) = newSym - Param(p.flags, newSym, p.sign, p.modulefulness) + Param(p.flags, newSym, p.sign, p.modulefulness)(p.toLoc) val refreshedParams = rewrittenParams.map: case ParamList(flags, params, restParam) => ParamList(flags, params.map(refreshParam), restParam.map(refreshParam)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 6f77793523..9cbffaa976 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -582,7 +582,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val fldSym = BlockMemberSymbol(nme, Nil) val tSym = TermSymbol(syntax.MutVal, S(clsSym), ident, erasedType = capturedType) - val p = Param(FldFlags.empty.copy(isVal = true), varSym, N, Modulefulness.none) + val p = Param(FldFlags.empty.copy(isVal = true), varSym, N, Modulefulness.none)(varSym.toLoc) varSym.decl = S(p) // * Currently this is only accessed to create the class' toString method val vd = ValDefn( @@ -909,7 +909,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): else b // some helpers - private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme), erasedType = p.sym.erasedType)) + private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme), erasedType = p.sym.erasedType))(p.toLoc) private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) private def dupParamList(plist: ParamList): ParamList = plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) @@ -1043,7 +1043,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): lazy val auxParams: List[Param] = (reqDefnsOrdered.map(defnSymsMap_) ::: capturesOrdered.map(capSymsMap_) ::: passedSymsOrdered.map(passedSymsMap_)) .map: s => - val decl = Param(FldFlags.empty.copy(isVal = false), s, N, Modulefulness.none) + val decl = Param(FldFlags.empty.copy(isVal = false), s, N, Modulefulness.none)(s.toLoc) s.decl = S(decl) decl diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index bcb61ec730..2d70936c9f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -321,21 +321,21 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): stagedAnnots, Nil, N, - ) + )(mod.toLoc) case _ => _defn reportAnnotations(defn, defn.extraAnnotations) val bufferableAnnots = defn.annotations.flatMap: case Annot.Trm(trm: SynthSel) => if trm.sym.contains(ctx.builtins.annotations.buffered) then - S(false) + S(false -> trm) else if trm.sym.contains(ctx.builtins.annotations.bufferable) then - S(true) + S(true -> trm) else N case _ => N if bufferableAnnots.length > 1 then raise(ErrorReport( - msg"Only one of bufferable annotation is allowed." -> defn.toLoc :: Nil, + msg"Only one of bufferable annotation is allowed." -> Loc(bufferableAnnots.map(_._2)) :: Nil, source = Diagnostic.Source.Compilation )) if bufferableAnnots.length >= 1 then @@ -344,7 +344,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"No companion class is allowed with @buffered or @bufferable." -> defn.toLoc :: Nil, source = Diagnostic.Source.Compilation )) - val bufferable = bufferableAnnots.headOption + val bufferable = bufferableAnnots.headOption.map(_._1) // Forbid @buffered classes from having a main parameter list bufferable.foreach: isBufferable => if !isBufferable && defn.paramsOpt.isDefined then @@ -525,7 +525,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case ps :: rest => val freshSyms = ps.params.map(p => new VarSymbol(new Tree.Ident(p.sym.nme), erasedType = N)) softTODO(ps.restParam.isEmpty, "Eta expanding rest parameters in constructor definitions is not yet supported") - val freshParams = (ps.params zip freshSyms).map((p, s) => Param(p.flags, s, N, p.modulefulness)) + val freshParams = (ps.params zip freshSyms).map((p, s) => Param(p.flags, s, N, p.modulefulness)(p.toLoc)) val freshParamList = ParamList(ps.flags, freshParams, N) val freshArgs = freshSyms.map(s => Arg(N, s.asSimpleRef)) Lambda(freshParamList, Return(etaExpand(rest, accArgss :+ freshArgs)))(Nil) @@ -635,8 +635,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): if sym.binary then val t1 = new Tree.Ident("arg1") val t2 = new Tree.Ident("arg2") - val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none) - val p2 = Param(FldFlags.empty, VarSymbol(t2, erasedType = N), N, Modulefulness.none) + val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none)(t1.toLoc) + val p2 = Param(FldFlags.empty, VarSymbol(t2, erasedType = N), N, Modulefulness.none)(t2.toLoc) val ps = PlainParamList(p1 :: p2 :: Nil) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, N).resolve, st.Ref(p2.sym)(t2, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) @@ -651,7 +651,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): return k(Lambda(paramLists.head, bodyBlock)(Nil).withLocOf(ref)) if sym.unary then val t1 = new Tree.Ident("arg") - val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none) + val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none)(t1.toLoc) val ps = PlainParamList(p1 :: Nil) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) @@ -740,7 +740,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): Modulefulness.none, Annot.RaiseEffects :: Nil, N, - ) + )(trm.toLoc) val rewritten = st.App( st.SynthSel(State.runtimeSymbol.ref(), Tree.Ident("toJsAsync"))(N, FlowSymbol.sel("toJsAsync"), N, N), st.Tup(PlainFld(st.Blk(td :: Nil, bms.ref(ident).resolved(dsym))) :: Nil)(Tree.DummyTup) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala index 80a83cf88d..0ee5b1028b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala @@ -47,7 +47,7 @@ class WorkerWrapper private def freshParam(param: Param, mapping: collection.mutable.Map[Symbol, Symbol]): Param = val freshSym = new VarSymbol(param.sym.id, erasedType = param.sym.erasedType) mapping(param.sym) = freshSym - Param(param.flags, freshSym, param.sign, param.modulefulness).withSignTypeOf(param) + Param(param.flags, freshSym, param.sign, param.modulefulness)(param.toLoc).withSignTypeOf(param) private def flattenParams(params: Ls[ParamList]): (ParamList, Map[Symbol, Symbol]) = val mapping = collection.mutable.LinkedHashMap.empty[Symbol, Symbol] 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 56adf7a882..12bcfab9af 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -487,7 +487,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) pl.params.map: p => val newSym = new VarSymbol(Tree.Ident(p.sym.name), erasedType = p.sym.erasedType) refreshParamMap(p.sym) = newSym - Param(p.flags, newSym, p.sign, p.modulefulness), + Param(p.flags, newSym, p.sign, p.modulefulness)(p.toLoc), pl.restParam) val bodyWithCorrectSymbols = refreshExtractedBody(refreshParamMap.toMap, rewrittenBody) FunDefn( diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 84759cc799..19079046e8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -560,7 +560,7 @@ object Elaborator: val id = new Ident("NonLocalReturn") val sym = ClassSymbol(DummyTypeDef(syntax.Cls), id) val bsym = BlockMemberSymbol("ret", Nil, true) - val defn = ClassDef(N, syntax.Cls, sym, bsym, N, Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), Nil, N, auxCtorParams = Nil) + val defn = ClassDef(N, syntax.Cls, sym, bsym, N, Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(UnitLit(false)))), Nil, N, auxCtorParams = Nil)(N) sym.defn = S(defn) Term.SynthSel(runtimeSymbol.ref(), id)(S(sym), FlowSymbol.synthSel(id.name), N, N) val nonLocalRet = @@ -734,7 +734,7 @@ extends Importer: Fun, mtdSym, tsym, - PlainParamList(valueSym.fold(Nil)(sym => Param(FldFlags.empty, sym, N, Modulefulness.none) :: Nil)) :: Nil, + PlainParamList(valueSym.fold(Nil)(sym => Param.simple(sym) :: Nil)) :: Nil, N, N, S(spec.methodBody(valueSym)), @@ -742,7 +742,7 @@ extends Importer: Modulefulness.none, Nil, N, - ) + )(N) tsym.defn = S(td) mtdSym.tsym = S(tsym) HandlerTermDefinition(resumeSym, td) @@ -1064,7 +1064,7 @@ extends Importer: given UnderCtx = new UnderCtx(S(unders)) val st = subterm(tree) val params = unders.iterator.map: sym => - Param(FldFlags.empty, sym, N, Modulefulness.none) + Param.simple(sym) .toList if params.isEmpty then st else Term.Lam(PlainParamList(params), st) @@ -1159,7 +1159,7 @@ extends Importer: derivedClsSym.defn = S(ClassDef( N, syntax.Cls, derivedClsSym, BlockMemberSymbol(derivedClsSym.name, Nil), N, - Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(Tree.UnitLit(false)))), Nil, N, auxCtorParams = Nil)) + Nil, Nil, N, ObjBody(Blk(Nil, Term.Lit(Tree.UnitLit(false)))), Nil, N, auxCtorParams = Nil)(hd.toLoc)) val elabed = ctx.nestInner(derivedClsSym).givenIn: block(sts_, hasResult = false)._1 @@ -1174,7 +1174,7 @@ extends Importer: case ParamList(_, value :: Nil, _) :: newParams => if newParams.isEmpty then raise(ErrorReport(msg"Handler function cannot be a getter" -> td.toLoc :: Nil)) - val newTd = TermDefinition(Fun, sym, tsym, newParams.reverse, tparams, sign, body, flags, mf, annotations, comp) + val newTd = TermDefinition(Fun, sym, tsym, newParams.reverse, tparams, sign, body, flags, mf, annotations, comp)(td.toLoc) S(HandlerTermDefinition(value.sym, newTd)) case _ => raise(ErrorReport(msg"Handler function is missing resumption parameter" -> td.toLoc :: Nil)) @@ -1220,7 +1220,7 @@ extends Importer: val boundVars = mutable.HashMap.empty[Str, VarSymbol] def genSym(id: Tree.Ident, erasedType: Opt[ErasedValueType]) = val sym = VarSymbol(id, erasedType) - sym.decl = S(TyParam(FldFlags.empty, N, sym)) // TODO vce + sym.decl = S(TyParam(FldFlags.empty, N, sym)(id.toLoc)) // TODO vce boundVars += id.name -> sym sym val syms = (tvs.collect: @@ -1418,9 +1418,9 @@ extends Importer: val self = VarSymbol(Ident("self"), erasedType = N) val args = VarSymbol(Ident("args"), erasedType = N) val ps = ParamList(ParamListFlags.empty, - Param(FldFlags.empty, self, N, Modulefulness.none) :: Nil, + Param.simple(self) :: Nil, S: - Param(FldFlags.empty, args, N, Modulefulness.none) + Param.simple(args) ) val rs = FlowSymbol.app() Term.Lam(ps, @@ -1491,7 +1491,7 @@ extends Importer: case tree @ Case(kw, _) => val scrut = VarSymbol(Ident("caseScrut"), erasedType = N) val body = caseSplit(scrut, tree) - val params = Param(FldFlags.empty, scrut, N, Modulefulness.none) :: Nil + val params = Param.simple(scrut) :: Nil Term.Lam(PlainParamList(params), body).mkLocWith(kw) case PrefixApp(kw @ Keywrd(Keyword.`return`), body) => ctx.getRetHandler match @@ -2090,7 +2090,7 @@ extends Importer: case _ => N val tsym = TermSymbol(k, owner, id, erasedType = erasedTpe) // TODO? val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body, - TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N).withLocOf(td) + TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N)(td.toLoc) tsym.defn = S(tdf) sym.tsym = S(tsym) @@ -2140,7 +2140,7 @@ extends Importer: ts.tys.flatMap: targ => def mk(id: Ident, vce: Opt[Bool]): Ls[TyParam] = val vs = VarSymbol(id, erasedType = N) - val res = TyParam(FldFlags.empty, vce, vs) + val res = TyParam(FldFlags.empty, vce, vs)(targ.toLoc) vs.decl = S(res) res :: Nil targ match @@ -2206,7 +2206,7 @@ extends Importer: p.modulefulness, Nil, N, - ).withLocOf(p) + )(p.toLoc) assert(p.fldSym.isEmpty) p.fldSym = S(fsym) fsym.tsym = S(tsym) @@ -2279,7 +2279,7 @@ extends Importer: assert(body.isEmpty) val d = given Ctx = newCtx - semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations) + semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations)(td.toLoc) alsSym.defn = S(d) d case Pat => @@ -2334,7 +2334,7 @@ extends Importer: // `paramsOpt` is set to `N` because we don't want parameters to // appear in the generated class's constructor. val pd = PatternDef(owner, patSym, sym, tps, allParams, - patternParams, extractionParams, pat, annotations) + patternParams, extractionParams, pat, annotations)(td.toLoc) patSym.defn = S(pd) pd case k: (Mod.type | Obj.type) => @@ -2350,7 +2350,7 @@ extends Importer: val md = val (bod, c) = mkBody(Nil) ModuleOrObjectDef(owner, modSym, sym, - tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx.scope) + tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx.scope, td.toLoc) modSym.defn = S(md) md case Cls => @@ -2386,7 +2386,7 @@ extends Importer: sym, ctsym, allCtorPss, - S(tps.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), + S(tps.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none)(tp.toLoc))), S(clsSym.ref()), N, TermDefFlags.empty, @@ -2395,7 +2395,7 @@ extends Importer: case a @ Annot.Modifier(Keyword.`declare`) => a , S(clsSym), - ) + )(td.toLoc) if pss.nonEmpty then sym.tsym = S(ctsym) ctsym.defn = S(ctdef) // Note: do NOT set sym.tsym for constructor(...) classes; they are not callable as functions. @@ -2403,7 +2403,7 @@ extends Importer: else N val cd = val (bod, c) = mkBody(auxCtorPss) - ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp, auxCtorParams = auxCtorPss) + ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp, auxCtorParams = auxCtorPss)(td.toLoc) clsSym.defn = S(cd) cd case Trt | Mxn => lastWords(s"Unexpected type definition kind here: $k") @@ -2494,7 +2494,7 @@ extends Importer: case N => sig.flatMap(ErasedType.eraseSign) val sym = VarSymbol(canonicalId, erasedType = erasedTpe) sym.sourceAliases = aliases - val p = Param(flg, sym, sig, mfn) + val p = Param(flg, sym, sig, mfn)(t.toLoc) sym.decl = S(p) (p, spd, aliases) @@ -2762,8 +2762,8 @@ extends Importer: val vs = ps.flatMap: case id: Ident => val sym = VarSymbol(id, erasedType = N) - sym.decl = S(TyParam(FldFlags.empty, N, sym)) - Param(FldFlags.empty, sym, N, Modulefulness.none) :: Nil + sym.decl = S(TyParam(FldFlags.empty, N, sym)(id.toLoc)) + Param.simple(sym) :: Nil case t => raise(ErrorReport(msg"Unsupported type parameter ${t.describe}" -> t.toLoc :: Nil)) Nil diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 3c181a9a6d..924f6aa611 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -267,7 +267,7 @@ object Resolvable: Nil, if defn.tparams.isEmpty then N - else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), + else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none)(tp.toLoc))), defn.rhs, TermDefFlags.empty, // TODO: handle class-like definitions with flags Modulefulness.none, // TODO: handle modulefulness for class-like definitions @@ -278,7 +278,7 @@ object Resolvable: defn.paramsOpt.toList ::: defn.auxParams, if defn.tparams.isEmpty then N - else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none))), + else S(defn.tparams.map(tp => Param(FldFlags.empty, tp.sym, N, Modulefulness.none)(tp.toLoc))), N, // TODO: handle class-like definitions with signatures TermDefFlags.empty, // TODO: handle class-like definitions with flags Modulefulness.none, // TODO: handle modulefulness for class-like definitions @@ -325,7 +325,7 @@ case class SrcScope(outer: Elaborator.OuterCtx, parent: Opt[SrcScope]): object SrcScope: given s: Ctx => SrcScope = summon[Ctx].scope -enum Term extends Statement: +enum Term extends Statement, AutoLocated: case Error() case UnitVal() case Missing // Placeholder terms that were not elaborated due to the "lightweight" elaboration mode `Mode.Light` @@ -646,7 +646,7 @@ object ShowCfg: end ShowCfg -sealed trait Statement extends AutoLocated, ProductWithExtraInfo: +sealed trait Statement extends Located, ProductWithExtraInfo: def mkClone(using State): Statement = this match case t: Term => lastWords(s"overridden implementation") @@ -984,16 +984,16 @@ sealed trait Statement extends AutoLocated, ProductWithExtraInfo: case LeadingDotSel(nme) => s"_?_.${nme.name}" case SetConfig(_) => "#config(...)" -final case class LetDecl(sym: LocalVarSymbol | TermSymbol, annotations: Ls[Annot]) extends Statement +final case class LetDecl(sym: LocalVarSymbol | TermSymbol, annotations: Ls[Annot]) extends Statement, AutoLocated -final case class RcdField(field: Term, rhs: Term) extends Statement -final case class RcdSpread(rcd: Term) extends Statement +final case class RcdField(field: Term, rhs: Term) extends Statement, AutoLocated +final case class RcdSpread(rcd: Term) extends Statement, AutoLocated -final case class DefineVar(sym: LocalSymbol | TermSymbol, rhs: Term) extends Statement +final case class DefineVar(sym: LocalSymbol | TermSymbol, rhs: Term) extends Statement, AutoLocated /** A global configuration change directive (`#config(...)`). * Records a function that modifies the current compiler configuration. */ -final case class SetConfig(modify: hkmc2.Config => hkmc2.Config) extends Statement: +final case class SetConfig(modify: hkmc2.Config => hkmc2.Config) extends Statement, AutoLocated: override def toString: String = "#config(...)" enum Visibility: @@ -1058,7 +1058,7 @@ final case class TermDefinition( modulefulness: Modulefulness, annotations: Ls[Annot], companion: Opt[CompanionSymbol], -) extends CompanionValue: +)(val toLoc: Opt[Loc]) extends CompanionValue: require(k is tsym.k) def bsym: BlockMemberSymbol = sym val owner = tsym.owner @@ -1143,10 +1143,15 @@ end ObjBody * in which case it is a `BlockMemberSymbol` when importing files explicitly * and a `TermSymbol` when the import is made implicitly by the compiler (eg, importing "Predef"). * Note that the `file` Path may not represent a real file; eg when importing "fs". */ -case class Import(sym: ImportSymbol, str: Str, file: io.Path) extends Statement +case class Import(sym: ImportSymbol, str: Str, file: io.Path) extends Statement, AutoLocated -sealed abstract class Declaration: +/** Declaration spans come from the syntax that introduced them, never from semantic + * subterms: those omit names and may contain synthesized or unrelated source terms. + * Rewrites preserve the original span; declarations without source syntax use `N`. + */ +sealed abstract class Declaration extends Located: + val toLoc: Opt[Loc] val sym: Symbol /** Whether this declares a class, a pattern, an object, or a pattern @@ -1218,7 +1223,8 @@ case class ModuleOrObjectDef( companion: Opt[ModuleCompanionSymbol], annotations: Ls[Annot], )( - val path: SrcScope + val path: SrcScope, + val toLoc: Opt[Loc], ) extends ClassLikeDef, CompanionValue: val ctorSym: Option[ClassCtorSymbol] = N @@ -1240,7 +1246,7 @@ case class PatternDef( */ pattern: Pattern, annotations: Ls[Annot], -) extends ClassLikeDef: +)(val toLoc: Opt[Loc]) extends ClassLikeDef: self => val kind: ClsLikeKind = Pat val ext: Opt[New] = N @@ -1286,14 +1292,14 @@ object ClassDef: annotations: Ls[Annot], comp: Opt[ClassCompanionSymbol], auxCtorParams: Ls[ParamList], - ): ClassDef = + )(toLoc: Opt[Loc]): ClassDef = params match case ps :: pss => Parameterized(owner, kind, sym.asInstanceOf// TODO: improve , bsym, S(ctorSym.getOrElse(lastWords("Parameterized classes should have a ctor symbol."))) - , tparams, ps, pss ::: auxCtorParams, ext, body, comp, annotations) + , tparams, ps, pss ::: auxCtorParams, ext, body, comp, annotations)(toLoc) case Nil => Plain(owner, kind, sym.asInstanceOf// TODO: improve , bsym - , tparams, ext, body, comp, annotations, auxParams = auxCtorParams, ctorSym = ctorSym) + , tparams, ext, body, comp, annotations, auxParams = auxCtorParams, ctorSym = ctorSym)(toLoc) def unapply(cls: ClassDef): Opt[(ClassSymbol, Ls[TyParam], Opt[ParamList], ObjBody)] = S((cls.sym, cls.tparams, cls.paramsOpt, cls.body)) @@ -1311,7 +1317,7 @@ object ClassDef: body: ObjBody, companion: Opt[ClassCompanionSymbol], annotations: Ls[Annot], - ) extends ClassDef: + )(val toLoc: Opt[Loc]) extends ClassDef: val paramsOpt: Opt[ParamList] = S(params) case class Plain( @@ -1326,7 +1332,7 @@ object ClassDef: annotations: Ls[Annot], auxParams: List[ParamList], ctorSym: Opt[ClassCtorSymbol], - ) extends ClassDef: + )(val toLoc: Opt[Loc]) extends ClassDef: val paramsOpt: Opt[ParamList] = N end ClassDef @@ -1339,7 +1345,7 @@ case class TypeDef( rhs: Opt[Term], companion: Opt[CompanionValue], annotations: Ls[Annot], -) extends TypeLikeDef: +)(val toLoc: Opt[Loc]) extends TypeLikeDef: val kind: ObjDefKind = Als @@ -1386,7 +1392,7 @@ final case class Spd(k: SpreadKind, term: Term) extends Elem: def showDbg(using DebugPrinter): Str = k.str + term.showDbg def children: Vector[Located] = Vector.single(term) -final case class TyParam(flags: FldFlags, vce: Opt[Bool], sym: VarSymbol) extends Declaration: +final case class TyParam(flags: FldFlags, vce: Opt[Bool], sym: VarSymbol)(val toLoc: Opt[Loc]) extends Declaration: // * For variance analysis var isCovariant: Bool = true @@ -1402,10 +1408,10 @@ final case class TyParam(flags: FldFlags, vce: Opt[Bool], sym: VarSymbol) extend object Param: - def simple(sym: VarSymbol) = Param(FldFlags.empty, sym, N, Modulefulness.none) + def simple(sym: VarSymbol) = Param(FldFlags.empty, sym, N, Modulefulness.none)(sym.toLoc) final case class Param(flags: FldFlags, sym: VarSymbol, sign: Opt[Term], modulefulness: Modulefulness) -extends Declaration, AutoLocated: +(val toLoc: Opt[Loc]) extends Declaration: var fldSym: Opt[MemberSymbol] = N val flow: FlowSymbol = sym @@ -1420,7 +1426,6 @@ extends Declaration, AutoLocated: def subTerms: Ls[Term] = sign.toList - override protected def children: Vector[Located] = sym +: sign.toVector def show(using Scope, ShowCfg, Raise): Document = doc"${flags.show(true)}${sym.showName}${sign.fold(doc"")(": " :: _.show)}" diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala index 201b34686f..eeaaca141c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala @@ -33,7 +33,7 @@ trait TermSynthesizer(using State): protected final def splitLet(sym: LocalVarSymbol, term: Term)(inner: Split): Split = Split.Let(sym, term, inner) - protected final def param = Param(FldFlags.empty, _, N, Modulefulness.none) + protected final def param = Param.simple protected final def paramList(params: Param*) = PlainParamList(params.toList) private lazy val runtimeRef: Term.Ref = State.runtimeSymbol.ref().resolve diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala index 2365c6343c..221c699958 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala @@ -793,7 +793,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz val symbols = pattern.variables.symbols val params = parameters.map: case (_, parameterSymbol) => - Param(FldFlags.empty, parameterSymbol, N, Modulefulness.none) + Param.simple(parameterSymbol) val lambdaSymbol = new TempSymbol(N, erasedType = N, "transform") // Next, we need to elaborate the pattern into a split. Note that // `makeMatchSplit` returns a function that takes a split as the @@ -1071,7 +1071,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz val symbols = pattern.variables.symbols val params = parameters.map: case (_, parameterSymbol) => - Param(FldFlags.empty, parameterSymbol, N, Modulefulness.none) + Param.simple(parameterSymbol) val lambdaSymbol = new TempSymbol(N, erasedType = N, "transform") (makeConsequent, alternative) => Split.Let( sym = lambdaSymbol, @@ -1247,9 +1247,9 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz ): (BlockMemberSymbol, ParamList, Split) = val sym = BlockMemberSymbol(name, Nil) // Pattern parameters are passed as objects. - val patternInputs = patternParameters.map(_.copy(flags = FldFlags.empty)) + val patternInputs = patternParameters.map(p => p.copy(flags = FldFlags.empty)(p.toLoc)) // The last parameter is the scrutinee. - val scrutParam = Param(FldFlags.empty, scrut, N, Modulefulness.none) + val scrutParam = Param.simple(scrut) val ps = PlainParamList(patternInputs :+ scrutParam) (sym, ps, topmost) @@ -1319,7 +1319,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz ): Ls[Statement] = val fieldSymbol = TempSymbol(N, erasedType = N, name) val decl = LetDecl(fieldSymbol, Nil) - val param = Param(FldFlags.empty, scrut, N, Modulefulness.none) + val param = Param.simple(scrut) val paramList = PlainParamList(param :: Nil) val lambda = Term.Lam(paramList, Term.SynthIf(topmost)) val defineVar = DefineVar(fieldSymbol, lambda) diff --git a/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls b/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls index 727dcefe98..47ed1cb990 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadTypeClasses.mls @@ -16,7 +16,7 @@ M.f //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.10: fun f(using Int) -//│ ║ ^^^ +//│ ║ ^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› :todo // support literal type @@ -78,7 +78,7 @@ f //│ ║ ^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.74: fun f[T](using t: T) = t -//│ ║ ^^^^ +//│ ║ ^^^^^^^^^^ //│ ╙── Illegal query for an unspecified type variable T. :e diff --git a/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls index 301fa0d67e..bf065ed09e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/CurriedClasses.mls @@ -107,10 +107,10 @@ class A(x1, x2, ...xs)(y1, ...ys) with fun fields = [x1, x2, xs, y1, ys] //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.106: class A(x1, x2, ...xs)(y1, ...ys) with -//│ ╙── ^^ +//│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.106: class A(x1, x2, ...xs)(y1, ...ys) with -//│ ╙── ^^ +//│ ╙── ^^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'xs' //│ ║ l.107: fun fields = [x1, x2, xs, y1, ys] //│ ║ ^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls index f1edfe839f..91640435b1 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ParamClasses.mls @@ -171,10 +171,10 @@ class Foo(val z, val z) //│ ╔══[COMPILATION ERROR] Duplicate definition of member named 'z'. //│ ╟── Defined at: //│ ║ l.170: class Foo(val z, val z) -//│ ║ ^ +//│ ║ ^^^^^ //│ ╟── Defined at: //│ ║ l.170: class Foo(val z, val z) -//│ ╙── ^ +//│ ╙── ^^^^^ Foo(1, 2) //│ = Foo(undefined, undefined) diff --git a/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls b/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls index e49546f47e..326b088f81 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/ThisCallVariations.mls @@ -61,7 +61,7 @@ data class Example2(val a) with //│ ╔══[COMPILATION ERROR] Duplicate definition of member named 'a'. //│ ╟── Defined at: //│ ║ l.58: data class Example2(val a) with -//│ ║ ^ +//│ ║ ^^^^^ //│ ╟── Defined at: //│ ║ l.59: val a = 1 //│ ╙── ^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ctx/CascadingTypeClasses.mls b/hkmc2/shared/src/test/mlscript/ctx/CascadingTypeClasses.mls index 02ec58b79f..53b3292247 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/CascadingTypeClasses.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/CascadingTypeClasses.mls @@ -61,7 +61,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.55: fun foo[A](using Foo[A])(using A) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -75,7 +75,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -90,7 +90,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -104,7 +104,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -118,7 +118,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -131,7 +131,7 @@ foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.69: fun foo[A](using A)(using Foo[A]) = 42 -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable A. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/ClassCtxParams.mls b/hkmc2/shared/src/test/mlscript/ctx/ClassCtxParams.mls index cb6e047e12..3f23709bb0 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/ClassCtxParams.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/ClassCtxParams.mls @@ -36,7 +36,7 @@ Bar //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.30: class Bar(using val s: Str) -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -49,7 +49,7 @@ Bar //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.43: class Bar(using s: Str) -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -87,7 +87,7 @@ Foo //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.79: class Foo(using Int)(using Str) with -//│ ║ ^^^ +//│ ║ ^^^^^^^^^ //│ ╙── Missing instance: Expected: Str; Available: Int //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -128,7 +128,7 @@ x => x.Foo#S //│ ║ ^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.101: class Foo(using T) with -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Missing instance: Expected: T; Available: Int //│ ————————————| JS (unsanitized) |———————————————————————————————————————————————————————————————————— //│ let lambda; lambda = (undefined, function (x) { return x.S }); return lambda diff --git a/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls b/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls index 8590db03b1..7c484266cd 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/EtaExpansion.mls @@ -16,7 +16,7 @@ baz //│ ║ ^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.9: fun baz(x)(using Int)(y)(using Num) = [x, use[Int], y, use[Num]] -//│ ║ ^^^ +//│ ║ ^^^^^^^^^ //│ ╙── Missing instance: Expected: Num; Available: Int //│ = fun diff --git a/hkmc2/shared/src/test/mlscript/ctx/ExplicitlySpec.mls b/hkmc2/shared/src/test/mlscript/ctx/ExplicitlySpec.mls index 97f2bf0ce6..c0577a3a0f 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/ExplicitlySpec.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/ExplicitlySpec.mls @@ -32,14 +32,14 @@ f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ╔══[COMPILATION ERROR] Cannot query instance of type Int for call: //│ ║ l.29: f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -51,7 +51,7 @@ f(using 32)(using 8) //│ ║ ^^^^^^^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.25: fun f(using i: Int)(using j: Int)(using k: Int): Int = i + j + k -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -70,7 +70,7 @@ f(using 32) //│ ║ ^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.63: fun f(using i: Int)(j: Int)(using k: Int)(l: Num): Int = i + j + k + l -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ = fun @@ -82,7 +82,7 @@ f(using 32)(8) //│ ║ ^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.63: fun f(using i: Int)(j: Int)(using k: Int)(l: Num): Int = i + j + k + l -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: ‹none available› //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/ForwardTypeClassUses.mls b/hkmc2/shared/src/test/mlscript/ctx/ForwardTypeClassUses.mls index 46e5ff8e47..d709788d87 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/ForwardTypeClassUses.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/ForwardTypeClassUses.mls @@ -35,7 +35,7 @@ test //│ ║ ^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.29: fun mtd()(using n: Int) = n -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -53,7 +53,7 @@ test //│ ║ ^^^^^^^^^^^^^^^^^^^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.47: fun mtd()(using n: Int) = n -//│ ║ ^^^^^^ +//│ ║ ^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Int; Available: Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/ctx/Summon.mls b/hkmc2/shared/src/test/mlscript/ctx/Summon.mls index 1e4afcc9a4..c992ee90aa 100644 --- a/hkmc2/shared/src/test/mlscript/ctx/Summon.mls +++ b/hkmc2/shared/src/test/mlscript/ctx/Summon.mls @@ -21,7 +21,7 @@ use[Num] //│ ╟── Required by contextual parameter declaration: //│ ║ in ../../mlscript-compile/Predef.mls //│ ║ l.133: fun use[T](using instance: T) = instance -//│ ║ ^^^^^^^^^^^ +//│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -58,7 +58,7 @@ fun f(using Int) = use[Num] //│ ╟── Required by contextual parameter declaration: //│ ║ in ../../mlscript-compile/Predef.mls //│ ║ l.133: fun use[T](using instance: T) = instance -//│ ║ ^^^^^^^^^^^ +//│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Missing instance: Expected: Num (type parameter T); Available: Int, Str @@ -88,7 +88,7 @@ use([Some[Int]].value) //│ ╟── Required by contextual parameter declaration: //│ ║ in ../../mlscript-compile/Predef.mls //│ ║ l.133: fun use[T](using instance: T) = instance -//│ ║ ^^^^^^^^^^^ +//│ ║ ^^^^^^^^^^^^^^^^^ //│ ╙── Illegal query for an unspecified type variable T. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -107,7 +107,7 @@ f //│ ║ ^ //│ ╟── Required by contextual parameter declaration: //│ ║ l.97: fun f[T](using T) = use[T] -//│ ║ ^ +//│ ║ ^^^^^^^ //│ ╙── Illegal query for an unspecified type variable T. //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 13fff6a256..dc13b50f26 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -247,7 +247,7 @@ res.a res.b //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.241: class A(...r) with -//│ ╙── ^ +//│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'r' //│ ║ l.242: fun a = r.push_back(x) //│ ║ ^ diff --git a/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls b/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls new file mode 100644 index 0000000000..de1cdffb91 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls @@ -0,0 +1,95 @@ +:. + +// An empty declaration still has a source span. +:e +module Empty +Empty.missing +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.6: Empty.missing +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── Module 'Empty' does not contain member 'missing' +//│ ║ l.5: module Empty +//│ ╙── ^^^^^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A member annotation must not become the enclosing module's location. +:e +module Container with + declare fun text(): Str +Container.missing +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.19: Container.missing +//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ╟── Module 'Container' does not contain member 'missing' +//│ ║ l.17: module Container with +//│ ╙── ^^^^^^^^^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// Nested declarations retain their own spans. +:e +module Outer with + module Inner with + declare fun text(): Str +Outer.Inner.missing +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.33: Outer.Inner.missing +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ╟── Module 'Inner' does not contain member 'missing' +//│ ║ l.31: module Inner with +//│ ╙── ^^^^^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// A nominal annotation continues to point at the use of the type. +:e +class Base with + declare fun text(): Str +fun hidden(value: Base) = value.missing +hidden(new Base) +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.46: fun hidden(value: Base) = value.missing +//│ ║ ^^^^^^^^^^^^^ +//│ ╟── Value of type 'Base' does not contain member 'missing' +//│ ║ l.46: fun hidden(value: Base) = value.missing +//│ ╙── ^^^^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. + +// The receiver inside a class uses the declaration, including its name and body. +:e +class Receiver with + declare fun text(): Str + fun inspect() = this.missing +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.60: fun inspect() = this.missing +//│ ║ ^^^^^^^^^^^^ +//│ ╟── Class definition 'Receiver' does not contain member 'missing' +//│ ║ l.58: class Receiver with +//│ ║ ^^^^^^^^^^^^^^^^^^^ +//│ ║ l.59: declare fun text(): Str +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.60: fun inspect() = this.missing +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Parameterized classes likewise use the written declaration, not only its fields. +:e +class Parameterized(value: Int) with + fun inspect() = this.missing +//│ ╔══[COMPILATION ERROR] Resolution error in selection +//│ ║ l.75: fun inspect() = this.missing +//│ ║ ^^^^^^^^^^^^ +//│ ╟── Class definition 'Parameterized' does not contain member 'missing' +//│ ║ l.74: class Parameterized(value: Int) with +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ║ l.75: fun inspect() = this.missing +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A class without fields has a declaration location even in pattern diagnostics. +:e +class Empty +if new Empty is Empty(_) then 1 else 0 +//│ ╔══[COMPILATION ERROR] Resolution error in constructor +//│ ║ l.88: if new Empty is Empty(_) then 1 else 0 +//│ ║ ^^^^^^^^ +//│ ╟── Class definition 'Empty' does not take pattern arguments. +//│ ║ l.87: class Empty +//│ ╙── ^^^^^^^^^^^ +//│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls index 953ed3a1c6..7b6e979b76 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls @@ -48,8 +48,8 @@ class A2(x) with @buffered class B0(x) //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//│ ║ l.48: @buffered -//│ ╙── ^^^^^^^^^ +//│ ║ l.49: class B0(x) +//│ ╙── ^^^^^^^^^^^ :ge @buffered @@ -61,38 +61,34 @@ class B1(x) //│ ║ l.56: @buffered //│ ╙── ^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//│ ║ l.55: @buffered -//│ ║ ^^^^^^^^^ -//│ ║ l.56: @buffered -//│ ╙── ^^^^^^^^^ +//│ ║ l.57: class B1(x) +//│ ╙── ^^^^^^^^^^^ :ge @buffered @bufferable class B2(x) //│ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. -//│ ║ l.70: @buffered +//│ ║ l.68: @buffered //│ ║ ^^^^^^^^^ -//│ ║ l.71: @bufferable +//│ ║ l.69: @bufferable //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//│ ║ l.70: @buffered -//│ ║ ^^^^^^^^^ -//│ ║ l.71: @bufferable +//│ ║ l.70: class B2(x) //│ ╙── ^^^^^^^^^^^ :w @bufferable module B3 //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.85: @bufferable +//│ ║ l.81: @bufferable //│ ╙── ^^^^^^^^^^^ :w @bufferable object B3 //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.92: @bufferable +//│ ║ l.88: @bufferable //│ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/parser/Handler.mls b/hkmc2/shared/src/test/mlscript/parser/Handler.mls index 58c06a2ee2..e424c7aa11 100644 --- a/hkmc2/shared/src/test/mlscript/parser/Handler.mls +++ b/hkmc2/shared/src/test/mlscript/parser/Handler.mls @@ -89,8 +89,10 @@ foo(h) //│ ║ l.84: val x = 24 //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Only function definitions are allowed in handler blocks +//│ ║ l.85: class Test with +//│ ║ ^^^^^^^^^^^^^^^ //│ ║ l.86: fun x() = x -//│ ╙── ^ +//│ ╙── ^^^^^^^^^^^^^^^ :e handle h = Eff with @@ -104,19 +106,19 @@ handle h = Eff with foo(h) foo(h) //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter -//│ ║ l.100: fun f = r(0) +//│ ║ l.102: fun f = r(0) //│ ╙── ^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter -//│ ║ l.101: fun g() = r(0) +//│ ║ l.103: fun g() = r(0) //│ ╙── ^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter -//│ ║ l.102: fun h()() = r(1) +//│ ║ l.104: fun h()() = r(1) //│ ╙── ^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Handler function is missing resumption parameter -//│ ║ l.103: fun h2()(a, b) = r(1) +//│ ║ l.105: fun h2()(a, b) = r(1) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: h -//│ ║ l.104: foo(h) +//│ ║ l.106: foo(h) //│ ╙── ^ :w @@ -127,6 +129,6 @@ handle h = Eff with 12345 foo(h) //│ ╔══[WARNING] Terms in handler block do nothing -//│ ║ l.127: 12345 +//│ ║ l.129: 12345 //│ ╙── ^^^^^ //│ Elab: { handle h = Ref(member:Eff)() List(HandlerTermDefinition(r,TermDefinition(Fun,member:f,term:Handler$h$/f,List(ParamList(‹›,List(),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(0)),None))))),‹method ›,Modulefulness(None),List(),None)), HandlerTermDefinition(r,TermDefinition(Fun,member:g,term:Handler$h$/g,List(ParamList(‹›,List(Param(‹›,a,None,Modulefulness(None))),None)),None,None,Some(App(Ref(r),Tup(List(Fld(‹›,Lit(IntLit(1)),None))))),‹method ›,Modulefulness(None),List(),None))) in App(Ref(member:foo),Tup(List(Fld(‹›,Ref(h),None)))) } From 3cd899886525be62d5d0b66e1fe91251293507ec Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 29 Sep 2026 22:28:46 +0800 Subject: [PATCH 2/5] Make annotation, statement, parameter-list and IR locations explicit --- .../src/main/scala/hkmc2/AsyncLowering.scala | 12 +- .../src/main/scala/hkmc2/CompilerCtx.scala | 4 +- .../src/main/scala/hkmc2/codegen/Block.scala | 158 ++++++++------- .../scala/hkmc2/codegen/BlockSimplifier.scala | 16 +- .../hkmc2/codegen/BlockTransformer.scala | 18 +- .../hkmc2/codegen/BufferableTransform.scala | 18 +- .../hkmc2/codegen/ClassParamFlattener.scala | 14 +- .../FirstClassFunctionTransformer.scala | 12 +- .../codegen/FlowAnalysisBasedRewrite.scala | 24 +-- .../scala/hkmc2/codegen/HandlerLowering.scala | 52 ++--- .../src/main/scala/hkmc2/codegen/Lifter.scala | 72 +++---- .../main/scala/hkmc2/codegen/Lowering.scala | 190 +++++++++--------- .../codegen/ReflectionInstrumenter.scala | 20 +- .../main/scala/hkmc2/codegen/TailRecOpt.scala | 36 ++-- .../scala/hkmc2/codegen/WorkerWrapper.scala | 10 +- .../hkmc2/codegen/deforest/Rewrite.scala | 18 +- .../scala/hkmc2/codegen/js/JSBuilder.scala | 10 +- .../hkmc2/codegen/wasm/text/WatBuilder.scala | 6 +- .../main/scala/hkmc2/invalml/InvalML.scala | 2 +- .../scala/hkmc2/semantics/Elaborator.scala | 82 ++++---- .../main/scala/hkmc2/semantics/Importer.scala | 12 +- .../src/main/scala/hkmc2/semantics/Term.scala | 101 ++++------ .../hkmc2/semantics/ucs/Normalization.scala | 20 +- .../hkmc2/semantics/ucs/TermSynthesizer.scala | 2 +- .../scala/hkmc2/semantics/ups/Compiler.scala | 8 +- .../semantics/ups/FixedPointCompiler.scala | 20 +- .../hkmc2/semantics/ups/SplitCompiler.scala | 12 +- .../src/main/scala/hkmc2/utils/utils.scala | 2 +- .../test/mlscript/block-staging/Functions.mls | 2 +- .../codegen/FirstClassFunctionTransform.mls | 6 +- .../test/mlscript/codegen/SourceLocations.mls | 43 ++++ .../mlscript/newres/DeclarationLocations.mls | 95 --------- .../src/test/mlscript/tailrec/Annots.mls | 42 ++-- .../test/mlscript/wasm/BuiltinOperators.mls | 4 +- .../test/scala/hkmc2/JSBackendDiffMaker.scala | 2 +- 35 files changed, 553 insertions(+), 592 deletions(-) create mode 100644 hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls delete mode 100644 hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls diff --git a/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala index fcee534a8e..9118221ce6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/AsyncLowering.scala @@ -56,22 +56,22 @@ class AsyncLowering(using TL, Raise, Elaborator.State, Elaborator.Ctx, Config): ) ) val vars = fun.params.flatMap(_.paramSyms) - val noAsync = fun.annotations.filterNot(_ is Annot.Async) + val noAsync = fun.annotations.filterNot(_.isInstanceOf[Annot.Async]) val transformer = new BlockTransformer(SymbolSubst.Id): override def applySimpleSymbol(sym: SimpleSymbol): SimpleSymbol = symMap.getOrElse(sym, sym) override def applyValue(v: Value)(k: Value => Block): Block = v match case Value.This(sym) if fun.owner.contains(sym) => - k(Value.SimpleRef(thisVar)) + k(Value.SimpleRef(thisVar)(v.toLoc)) case _ => super.applyValue(v)(k) val newBody = transformer.applyBlock(wrapAwait(true)(applyFunBodyLikeBlock(fun.body))) - collectedFunDefn += FunDefn(N, outerBms, outerDsym, PlainParamList((thisParam.iterator ++ outerParams.iterator.map(_._2)).toList) :: PlainParamList(Nil) :: Nil, newBody)(fun.configOverride, noAsync) - val callArgs = (fun.owner.iterator.map(s => Arg(N, Value.This(s))) ++ fun.params.iterator.flatMap(_.allParams.iterator.map(p => Arg(N, Value.SimpleRef(p.sym))))).toList - val outerCall = Call(Value.MemberRef(outerBms, outerDsym), callArgs ne_:: Nil)(CallMetadata.mlsFunWithEffect) + collectedFunDefn += FunDefn(N, outerBms, outerDsym, PlainParamList((thisParam.iterator ++ outerParams.iterator.map(_._2)).toList)(N) :: PlainParamList(Nil)(N) :: Nil, newBody)(fun.configOverride, noAsync) + val callArgs = (fun.owner.iterator.map(s => Arg(N, Value.This(s)(N))) ++ fun.params.iterator.flatMap(_.allParams.iterator.map(p => Arg(N, Value.SimpleRef(p.sym)(N))))).toList + val outerCall = Call(Value.MemberRef(outerBms, outerDsym)(N), callArgs ne_:: Nil)(CallMetadata.mlsFunWithEffect, N) val tmp = TempSymbol(N, erasedType = outerCall.erasedValueType, "tmp") val wrapperBody = blockBuilder .assignScoped(tmp, outerCall) - .ret(Call(Value.SimpleRef(State.runtimeSymbol).selSN("toJsAsync"), (tmp.asSimpleRef.asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun)) + .ret(Call(Value.SimpleRef(State.runtimeSymbol)(N).selSN("toJsAsync"), (tmp.asSimpleRef.asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N)) FunDefn(fun.owner, fun.sym, fun.dSym, fun.params, wrapperBody)(fun.configOverride, noAsync) override def applyMainBlock(main: Block): Block = diff --git a/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala b/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala index 0c65a0e3c9..5f277a130d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala @@ -121,10 +121,10 @@ class CompilerCtx( case _ => t.subTerms.exists(findQuote) val hasQuote = findQuote(blk0) val blk = new Term.Blk( - Import(State.runtimeSymbol, paths.runtimeFile.toString, paths.runtimeFile) :: + Import(State.runtimeSymbol, paths.runtimeFile.toString, paths.runtimeFile)(N) :: // Only import `Term.mls` when necessary. (if hasQuote then - Import(State.termSymbol, paths.termFile.toString, paths.termFile) :: blk0.stats + Import(State.termSymbol, paths.termFile.toString, paths.termFile)(N) :: blk0.stats else blk0.stats), blk0.res diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 1c099ed4d9..65c9376572 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -405,8 +405,8 @@ object Throw: def error(msg: Str)(using State): Throw = Throw(Instantiate( mut = false, State.globalThisSymbol.asThis.selN(Tree.Ident("Error")), - (Value.Lit(Tree.StrLit(msg)).asArg :: Nil) :: Nil, - )(InstantiateMetadata.empty)) + (Value.Lit(Tree.StrLit(msg))(N).asArg :: Nil) :: Nil, + )(InstantiateMetadata.empty, N)) case class Label(label: LabelSymbol, loop: Bool, body: Block, rest: Block) extends Block with NonBlockTail with ProductWithTail @@ -559,11 +559,11 @@ object HandleBlock: def suspend(tag: Path, handlerFun: Path)(using Elaborator.Ctx): Result = val bms = Elaborator.ctx.builtins.runtime.suspend - Call(bms.asMemberRef(bms.asPrincipal.get), (tag.asArg :: handlerFun.asArg :: Nil) ne_:: Nil)(CallMetadata.mlsFunWithEffect) + Call(bms.asMemberRef(bms.asPrincipal.get), (tag.asArg :: handlerFun.asArg :: Nil) ne_:: Nil)(CallMetadata.mlsFunWithEffect, N) def handleSuspension(tag: Path, bodyFun: Path)(using Elaborator.Ctx): Result = val bms = Elaborator.ctx.builtins.runtime.handle_suspension - Call(bms.asMemberRef(bms.asPrincipal.get), (tag.asArg :: bodyFun.asArg :: Nil) ne_:: Nil)(CallMetadata.mlsFunWithEffect) + Call(bms.asMemberRef(bms.asPrincipal.get), (tag.asArg :: bodyFun.asArg :: Nil) ne_:: Nil)(CallMetadata.mlsFunWithEffect, N) private def create( lhs: LocalVarSymbol, @@ -577,12 +577,12 @@ object HandleBlock: )(using Elaborator.State, Elaborator.Ctx) = val sym = new BlockMemberSymbol("handleBlock$", Nil, false) - val bodyDefn = FunDefn.withFreshSymbol(N, sym, PlainParamList(Nil) :: Nil, body)(N, annotations = Nil) + val bodyDefn = FunDefn.withFreshSymbol(N, sym, PlainParamList(Nil)(N) :: Nil, body)(N, annotations = Nil) val handlerMtds = handlers.map: handler => val sym = BlockMemberSymbol(cls.nme + handler.sym.nme, Nil, true) val fDef = FunDefn.withFreshSymbol( - N, sym, PlainParamList(Param.simple(handler.resumeSym) :: Nil) :: Nil, + N, sym, PlainParamList(Param.simple(handler.resumeSym) :: Nil)(N) :: Nil, handler.body )(N, annotations = Nil) val rSym = TempSymbol(N, erasedType = N, "suspendRes") @@ -603,7 +603,7 @@ object HandleBlock: N, Nil, S(par), handlerMtds, Nil, Nil, // Apparently, the lifter is not happy with any assignment in the preCtor... - Assign(NoSymbol, Call(State.builtinOpsMap("super").asSimpleRef, args.map(_.asArg) ne_:: Nil)(CallMetadata.mlsFunWithEffect), End()), + Assign(NoSymbol, Call(State.builtinOpsMap("super").asSimpleRef, args.map(_.asArg) ne_:: Nil)(CallMetadata.mlsFunWithEffect, N), End()), End(), N, N, @@ -612,7 +612,7 @@ object HandleBlock: blockBuilder .scopedVars(Set(clsDefn.sym, sym)) .define(clsDefn) - .assign(lhs, Instantiate(mut = true, clsDefn.sym.asMemberRef(cls), Nil :: Nil)(InstantiateMetadata.empty)) + .assign(lhs, Instantiate(mut = true, clsDefn.sym.asMemberRef(cls), Nil :: Nil)(InstantiateMetadata.empty, N)) .define(bodyDefn) .assign(res, handleSuspension(lhs.asSimpleRef, bodyDefn.sym.asMemberRef(bodyDefn.dSym))) .rest(rest) @@ -706,11 +706,11 @@ final case class FunDefn( ) extends Defn: val defnSym = S(dSym) val asPath = sym.asMemberRef(dSym) - lazy val tailRec: Bool = annotations.contains(Annot.TailRec) - lazy val inline: Bool = annotations.contains(Annot.Inline) - lazy val noInline: Bool = annotations.contains(Annot.NoInline) || generator || async - lazy val generator: Bool = annotations.contains(Annot.Generator) - lazy val async: Bool = annotations.contains(Annot.Async) + lazy val tailRec: Bool = annotations.exists(_.isInstanceOf[Annot.TailRec]) + lazy val inline: Bool = annotations.exists(_.isInstanceOf[Annot.Inline]) + lazy val noInline: Bool = annotations.exists(_.isInstanceOf[Annot.NoInline]) || generator || async + lazy val generator: Bool = annotations.exists(_.isInstanceOf[Annot.Generator]) + lazy val async: Bool = annotations.exists(_.isInstanceOf[Annot.Async]) lazy val affineInfo: Ls[Int] = annotations.collect: case Annot.Affine(whichParamList) => whichParamList @@ -976,7 +976,14 @@ enum Case: sealed trait TrivialResult extends Result -sealed abstract class Result extends AutoLocated, HasErasedType: +/** The source expression that produced an IR result is independent of its operands. + * Rewrites preserve this span; generated results with no source expression use `N`. + */ +sealed abstract class Result extends Located, HasErasedType: + val toLoc: Opt[Loc] + /** Return a result with explicit provenance without mutating a shared IR node. */ + def withLoc(loc: Opt[Loc]): Result + def withLocOf(source: Located): Result = withLoc(source.toLoc) // // * Used for debugging locations: // sealed abstract class Result extends AutoLocated with ProductWithExtraInfo: // def extraInfo: Str = toLoc.toString @@ -1026,24 +1033,6 @@ sealed abstract class Result extends AutoLocated, HasErasedType: // case Instantiate(mut, cls, args) => // TODO? case _ => false - // * Note: this function is used to piece together a location; - // * for the location to be valid, we should NOT have it include children whose location - // * is from some different place (with a different Origin), such as the location attached to symbols. - // * That's why for example, we're not adding the `l` of `Value.Ref` to the children list. - protected def children: Vector[Located] = this match - case Call(fun, argss) => fun +: argss.iterator.flatten.map(_.value).toVector - case Instantiate(mut, cls, argss) => cls +: argss.iterator.flatten.map(_.value).toVector - case Cast(value, target, _) => Vector.single(value) - case Select(qual, name) => Vector.double(qual, name) - case DynSelect(qual, fld, arrayIdx) => Vector.double(qual, fld) - case Lambda(params, body) => Vector.single(params) - case Tuple(mut, elems) => elems.iterator.map(_.value).toVector - case Record(mut, elems) => elems.iterator.map(_.value).toVector - case Value.SimpleRef(l) => Vector.empty - case Value.MemberRef(bms, disamb) => Vector.empty - case Value.This(sym) => Vector.empty - case Value.Lit(lit) => Vector.single(lit) - // TODO rm Lam from values and thus the need for this method def subBlocks: Ls[Block] = this match case Call(fun, argss) => fun.subBlocks ::: argss.flatten.flatMap(_.value.subBlocks) @@ -1151,7 +1140,7 @@ sealed abstract class Result extends AutoLocated, HasErasedType: val target = expected match case ft: ErasedFuncType => ErasedType.Function(ft.rsc) case v: ErasedValueType => v - Cast(this, target, config.checkCasts) + Cast(this, target, config.checkCasts)(loc) case N => // * An `Incompatible` side is not an unrelated type but an unrepresentable one, so it gets its own message. def membersOf(et: CanonicalErasedType): Opt[(CanonicalErasedValueType, CanonicalErasedValueType)] = et match @@ -1173,7 +1162,7 @@ case class CallMetadata( mayRaiseEffects: Bool, annotations: Ls[Annot], ): - lazy val explicitTailCall: Bool = annotations.contains(Annot.TailCall) + lazy val explicitTailCall: Bool = annotations.exists(_.isInstanceOf[Annot.TailCall]) object CallMetadata: val defaultMlsFun = CallMetadata(true, false, Nil) @@ -1181,7 +1170,8 @@ object CallMetadata: val mlsFunWithEffect = CallMetadata(true, true, Nil) -case class Call(fun: Path, argss: NELs[Ls[Arg]])(val metadata: CallMetadata) extends Result: +case class Call(fun: Path, argss: NELs[Ls[Arg]])(val metadata: CallMetadata, val toLoc: Opt[Loc]) extends Result: + def withLoc(loc: Opt[Loc]): Call = if loc == toLoc then this else copy()(metadata, loc) lazy val isKnownUnsaturatedCall: Bool = fun.targetSymbol match case S(ts: TermSymbol) => @@ -1202,20 +1192,20 @@ case class Call(fun: Path, argss: NELs[Ls[Arg]])(val metadata: CallMetadata) ext object Call: - def raw(fun: Path, argss: NELs[Ls[Arg]])(metadata: CallMetadata): Call = - new Call(fun, argss)(metadata) + def raw(fun: Path, argss: NELs[Ls[Arg]])(metadata: CallMetadata, toLoc: Opt[Loc]): Call = + new Call(fun, argss)(metadata, toLoc) - def apply(fun: Path, argss: NELs[Ls[Arg]])(metadata: CallMetadata): Result = + def apply(fun: Path, argss: NELs[Ls[Arg]])(metadata: CallMetadata, toLoc: Opt[Loc]): Result = fun match case Value.SimpleRef(sym: BuiltinSymbol) => argss match case (Arg(N, arg1: Value) :: Arg(N, arg2: Value) :: Nil) :: Nil => - evalBuiltin(sym, arg1, arg2)(return _) + evalBuiltin(sym, arg1, arg2)(value => return value.withLoc(toLoc)) case (Arg(N, arg1: Value) :: Nil) :: Nil => - evalBuiltin(sym, arg1)(return _) + evalBuiltin(sym, arg1)(value => return value.withLoc(toLoc)) case _ => case _ => - raw(fun, argss)(metadata) + raw(fun, argss)(metadata, toLoc) private def literalArgValues(args: Ls[Arg]): Opt[Ls[Value]] = args.foldRight[Opt[Ls[Value]]](S(Nil)): @@ -1226,27 +1216,27 @@ object Call: private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value, arg2: Value)(inline k: Value => Unit): Unit = (sym.nme, arg1, arg2) match - case ("+", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 + v2))) - case ("+", Lit(Tree.StrLit(v1)), Lit(Tree.StrLit(v2))) => k(Lit(Tree.StrLit(v1 + v2))) - case ("-", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 - v2))) - case ("*", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 * v2))) + case ("+", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 + v2))(N)) + case ("+", Lit(Tree.StrLit(v1)), Lit(Tree.StrLit(v2))) => k(Lit(Tree.StrLit(v1 + v2))(N)) + case ("-", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 - v2))(N)) + case ("*", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 * v2))(N)) // * For "/", should check for 0 and return a DecLit. - case ("%", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) if v2 =/= 0 => k(Lit(Tree.IntLit(v1 % v2))) - case ("===", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 == l2))) - case ("!==", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 != l2))) - case ("<", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 < v2))) - case ("<=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 <= v2))) - case (">", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 > v2))) - case (">=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 >= v2))) - case ("&&", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 && v2))) - case ("||", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 || v2))) + case ("%", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) if v2 =/= 0 => k(Lit(Tree.IntLit(v1 % v2))(N)) + case ("===", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 == l2))(N)) + case ("!==", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 != l2))(N)) + case ("<", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 < v2))(N)) + case ("<=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 <= v2))(N)) + case (">", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 > v2))(N)) + case (">=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 >= v2))(N)) + case ("&&", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 && v2))(N)) + case ("||", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 || v2))(N)) case _ => private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value)(inline k: Value => Unit): Unit = (sym.nme, arg1) match - case ("+", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(v1))) - case ("-", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(-v1))) - case ("!", Lit(Tree.BoolLit(v))) => k(Lit(Tree.BoolLit(!v))) + case ("+", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(v1))(N)) + case ("-", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(-v1))(N)) + case ("!", Lit(Tree.BoolLit(v))) => k(Lit(Tree.BoolLit(!v))(N)) case _ => end Call @@ -1259,7 +1249,8 @@ case class InstantiateMetadata( object InstantiateMetadata: def empty: InstantiateMetadata = InstantiateMetadata(Nil) -case class Instantiate(mut: Bool, cls: Path, argss: Ls[Ls[Arg]])(val metadata: InstantiateMetadata) extends Result +case class Instantiate(mut: Bool, cls: Path, argss: Ls[Ls[Arg]])(val metadata: InstantiateMetadata, val toLoc: Opt[Loc]) extends Result: + def withLoc(loc: Opt[Loc]): Instantiate = if loc == toLoc then this else copy()(metadata, loc) /** A coercion of `value` to `target`. * @@ -1277,7 +1268,8 @@ case class Instantiate(mut: Bool, cls: Path, argss: Ls[Ls[Arg]])(val metadata: I * - `value` is not a `Cast`. * - `target` must be a proper subtype of `value`'s erased type. */ -case class Cast private(value: Result, target: ErasedValueType, check: Bool) extends Path +case class Cast private(value: Result, target: ErasedValueType, check: Bool)(val toLoc: Opt[Loc]) extends Path: + def withLoc(loc: Opt[Loc]): Cast = if loc == toLoc then this else copy()(loc) object Cast: /** Builds a cast while collapsing a nested cast. @@ -1291,23 +1283,28 @@ object Cast: * * Note that explicitly-checked casts are never lost to preserve the semantics of eagerly failing when casts fail. */ - def apply(value: Result, target: ErasedValueType, check: Bool): Cast = + def apply(value: Result, target: ErasedValueType, check: Bool)(toLoc: Opt[Loc]): Cast = value match - case Cast(inner, _, innerCheck) => new Cast(inner, target, check || innerCheck) - case _ => new Cast(value, target, check) + case Cast(inner, _, innerCheck) => new Cast(inner, target, check || innerCheck)(toLoc) + case _ => new Cast(value, target, check)(toLoc) -case class Lambda(params: ParamList, body: Block)(val annot: Ls[Annot]) extends Result: +case class Lambda(params: ParamList, body: Block)(val annot: Ls[Annot], val toLoc: Opt[Loc]) extends Result: + def withLoc(loc: Opt[Loc]): Lambda = if loc == toLoc then this else copy()(annot, loc) lazy val affine: Bool = annot.exists(_.isInstanceOf[Annot.Affine]) -case class Tuple(mut: Bool, elems: Ls[Arg]) extends Result +case class Tuple(mut: Bool, elems: Ls[Arg])(val toLoc: Opt[Loc]) extends Result: + def withLoc(loc: Opt[Loc]): Tuple = if loc == toLoc then this else copy()(loc) -case class Record(mut: Bool, elems: Ls[RcdArg]) extends Result +case class Record(mut: Bool, elems: Ls[RcdArg])(val toLoc: Opt[Loc]) extends Result: + def withLoc(loc: Opt[Loc]): Record = if loc == toLoc then this else copy()(loc) sealed abstract class Path extends TrivialResult: - def selN(id: Tree.Ident): Path = Select(this, id)(N)(false) - def sel(id: Tree.Ident, sym: DefinitionSymbol[?]): Path = Select(this, id)(S(sym))(false) + override def withLoc(loc: Opt[Loc]): Path + override def withLocOf(source: Located): Path = withLoc(source.toLoc) + def selN(id: Tree.Ident): Path = Select(this, id)(N, N)(false) + def sel(id: Tree.Ident, sym: DefinitionSymbol[?]): Path = Select(this, id)(S(sym), N)(false) def selSN(id: Str): Path = selN(new Tree.Ident(id)) def asArg = Arg(spread = N, this) def targetSymbol: Opt[DefinitionSymbol[?]] = this match @@ -1318,19 +1315,28 @@ sealed abstract class Path extends TrivialResult: /** * @param symbol The symbol representing the definition that the selection refers to, if known. */ -case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[DefinitionSymbol[?]])(val sanitize: Boolean) extends Path with ProductWithExtraInfo: +case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[DefinitionSymbol[?]], val toLoc: Opt[Loc])(val sanitize: Boolean) extends Path with ProductWithExtraInfo: + def withLoc(loc: Opt[Loc]): Select = if loc == toLoc then this else copy()(symbol, loc)(sanitize) def extraInfo(using DebugPrinter): Str = symbol.map(s => s"sym=${s.showAsPlain}").mkString -case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool) extends Path +case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool)(val toLoc: Opt[Loc]) extends Path: + def withLoc(loc: Opt[Loc]): DynSelect = if loc == toLoc then this else copy()(loc) enum Value extends Path with ProductWithExtraInfo: - case SimpleRef(sym: SimpleSymbol) + case SimpleRef(sym: SimpleSymbol)(val toLoc: Opt[Loc]) /** * @param disamb The symbol disambiguating the definition that the reference refers to. */ - case MemberRef(bms: BlockMemberSymbol, disamb: DefinitionSymbol[?]) - case This(sym: InnerSymbol) - case Lit(lit: Literal) + case MemberRef(bms: BlockMemberSymbol, disamb: DefinitionSymbol[?])(val toLoc: Opt[Loc]) + case This(sym: InnerSymbol)(val toLoc: Opt[Loc]) + case Lit(lit: Literal)(val toLoc: Opt[Loc]) + + override def withLoc(loc: Opt[Loc]): Value = if loc == toLoc then this else this match + case v: SimpleRef => v.copy()(loc) + case v: MemberRef => v.copy()(loc) + case v: This => v.copy()(loc) + case v: Lit => v.copy()(loc) + override def withLocOf(source: Located): Value = withLoc(source.toLoc) override def extraInfo(using DebugPrinter): Str = this match case MemberRef(bms, disamb) => s"disamb=${disamb.showAsPlain}" @@ -1419,13 +1425,13 @@ extension (k: Block => Block) def blockBuilder: Block => Block = identity extension (s: SimpleSymbol) - inline def asSimpleRef: Value.SimpleRef = Value.SimpleRef(s) + inline def asSimpleRef: Value.SimpleRef = Value.SimpleRef(s)(N) extension (bms: BlockMemberSymbol) - inline def asMemberRef(disamb: DefinitionSymbol[?]): Value.MemberRef = Value.MemberRef(bms, disamb) + inline def asMemberRef(disamb: DefinitionSymbol[?]): Value.MemberRef = Value.MemberRef(bms, disamb)(N) extension (sym: InnerSymbol) - inline def asThis: Value.This = Value.This(sym) + inline def asThis: Value.This = Value.This(sym)(N) extension (l: ValueSymbol) // TODO(Derppening): Inline `Value.Ref.apply` into this function once that function is removed diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala index 1ecdad3993..fab852731a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockSimplifier.scala @@ -289,7 +289,7 @@ class BlockSimplifier case Value.SimpleRef(loc: LocalVarSymbol) if localVars.contains(loc) && !definedVars.contains(loc) => registerChange(s"${loc.showDbg} is never assigned; replacing read with undefined") // if !symbolsToPreserve(loc) then removedLocals += loc - k(Value.Lit(syntax.Tree.UnitLit(false))) + k(Value.Lit(syntax.Tree.UnitLit(false))(v.toLoc)) case _ => super.applyValue(v)(k) override def applyBlock(b: Block): Block = b match @@ -863,7 +863,7 @@ class BlockSimplifier registerChange(s"immediate assigned call prefix ${lhs.showDbg} ~> ${path.showDbg}") applyPath(path): path2 => val lhs2 = recordAssignmentFact(lhs, path2, ass) - val combined = Call(path2, argss)(call.metadata).withLocOf(call) + val combined = Call(path2, argss)(call.metadata, call.toLoc) val res = applyBlock(Assign(nextLhs, combined, rst)) // * Note that it is incorrect to eliminate the `lhs` assignment even if `!rst.freeVars(lhs)`, // * because the assignment may be visible from an outer block @@ -878,7 +878,7 @@ class BlockSimplifier registerChange(s"immediate returned call prefix ${lhs.showDbg} ~> ${path.showDbg}") applyPath(path): path2 => val lhs2 = recordAssignmentFact(lhs, path2, ass) - val combined = Call(path2, argss)(call.metadata).withLocOf(call) + val combined = Call(path2, argss)(call.metadata, call.toLoc) val res = applyBlock(Return(combined)) if symbolsToPreserve(lhs) then Assign(lhs2, path2, res) else res @@ -1157,7 +1157,7 @@ class BlockSimplifier analysis.litValue match case true => registerChange(s"${loc.showDbg} ~> undefined") - return k(Value.Lit(syntax.Tree.UnitLit(false))) + return k(Value.Lit(syntax.Tree.UnitLit(false))(v.toLoc)) case lit: (Value | Cast) => registerChange(s"${loc.showDbg} ~> ${lit.showDbg}") return k(lit) @@ -1203,7 +1203,7 @@ class BlockSimplifier prefix.metadata.mayRaiseEffects || c.metadata.mayRaiseEffects, prefix.metadata.annotations ++ c.metadata.annotations, ), - ).withLocOf(c) + c.toLoc) super.applyResult(combined)(k) case N => super.applyResult(r)(k) @@ -1474,7 +1474,7 @@ class BlockSimplifier if args.size < params.params.size then return N val (fixedArgs, restArgs) = args.splitAt(params.params.size) S(fixedArgs.zip(params.params).map((arg, param) => (param.sym, arg.value)) ++ - List((params.restParam.get.sym, Tuple(true, restArgs)))) + List((params.restParam.get.sym, Tuple(true, restArgs)(N)))) /** Match multiple argument lists against multiple parameter lists. * Returns None if any arg list fails to match its corresponding param list, @@ -1917,8 +1917,8 @@ class BlockSimplifier acc(Scoped(Set(resSym), newBlk( k(Call(resSym.asSimpleRef, extraArgss.ne_!)( call.metadata.copy( - annotations = call.metadata.annotations.filterNot(_ == Annot.TailCall), - )))))) + annotations = call.metadata.annotations.filterNot(_.isInstanceOf[Annot.TailCall]), + ), call.toLoc))))) case (sym, value) :: argRest => val newSym = VarSymbol(sym.id, erasedType = sym.erasedType) go(acc.assignScoped(newSym, value), argRest, mapping + (sym -> newSym)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala index 8f3588070a..0f863beadc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BlockTransformer.scala @@ -164,33 +164,33 @@ class BlockTransformer(subst: SymbolSubst): applyPath(fun): fun2 => applyListOf(argss, (args, k2) => applyArgs(args)(k2)): argss2 => k(if (fun2 is fun) && (argss2 is argss) then r - else Call(fun2, argss2.ne_!)(r.metadata).withLocOf(r)) + else Call(fun2, argss2.ne_!)(r.metadata, r.toLoc)) case r @ Instantiate(mut, cls, argss) => applyPath(cls): cls2 => applyListOf(argss, (args, k2) => applyArgs(args)(k2)): argss2 => k(if (cls2 is cls) && (argss2 is argss) then r - else Instantiate(mut, cls2, argss2)(r.metadata).withLocOf(r)) + else Instantiate(mut, cls2, argss2)(r.metadata, r.toLoc)) case l: Lambda => k(applyLam(l)) case Tuple(mut, elems) => applyArgs(elems): elems2 => - k(if (elems2 is elems) then r else Tuple(mut, elems2).withLocOf(r)) + k(if (elems2 is elems) then r else Tuple(mut, elems2)(r.toLoc)) case Record(mut, fields) => applyRcdArgs(fields): fields2 => - k(if fields2 is fields then r else Record(mut, fields2).withLocOf(r)) + k(if fields2 is fields then r else Record(mut, fields2)(r.toLoc)) case p: Path => applyPath(p)(k) def applyPath(p: Path)(k: Path => Block): Block = p match case DynSelect(qual, fld, arrayIdx) => applyPath(qual): qual2 => applyPath(fld): fld2 => - k(if (qual2 is qual) && (fld2 is fld) then p else DynSelect(qual2, fld2, arrayIdx).withLocOf(p)) + k(if (qual2 is qual) && (fld2 is fld) then p else DynSelect(qual2, fld2, arrayIdx)(p.toLoc)) case p @ Select(qual, name) => applyPath(qual): qual2 => val sym2 = p.symbol.mapConserve(_.subst) - k(if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2)(p.sanitize).withLocOf(p)) + k(if (qual2 is qual) && (sym2 is p.symbol) then p else Select(qual2, name)(sym2, p.toLoc)(p.sanitize)) case c @ Cast(value, target, check) => applyResult(value): value2 => - k(if value2 is value then c else Cast(value2, target, check).withLocOf(c)) + k(if value2 is value then c else Cast(value2, target, check)(c.toLoc)) case v: Value => applyValue(v)(k) def applyValue(v: Value)(k: Value => Block) = v match @@ -304,7 +304,7 @@ class BlockTransformer(subst: SymbolSubst): val params2 = pl.params.mapConserve(applyParam) val rest2 = pl.restParam.mapConserve(applyParam) if (params2 is pl.params) && (rest2 is pl.restParam) - then pl else ParamList(pl.flags, params2, rest2) + then pl else ParamList(pl.flags, params2, rest2)(pl.toLoc) def applyCase(cse: Case)(k: Case => Block): Block = cse match case Case.Lit(lit) => k(cse) @@ -327,7 +327,7 @@ class BlockTransformer(subst: SymbolSubst): def applyLam(lam: Lambda): Lambda = val params2 = applyParamList(lam.params) val body2 = applyFunBodyLikeBlock(lam.body) - if (params2 is lam.params) && (body2 is lam.body) then lam else Lambda(params2, body2)(lam.annot) + if (params2 is lam.params) && (body2 is lam.body) then lam else Lambda(params2, body2)(lam.annot, lam.toLoc) def applyListOf[A](ls: List[A], f: (A, (A => Block)) => Block)(k: List[A] => Block): Block = def rec(ls: List[A], k: List[A] => Block): Block = ls match diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala index aa9b81d710..800f5ff664 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/BufferableTransform.scala @@ -35,15 +35,15 @@ class BufferableTransform()(using State, Raise): .toMap def mapParam(p: Param) = Param(p.flags, varMap(p.sym), p.sign, p.modulefulness)(p.toLoc) - (params.map(pl => ParamList(pl.flags, pl.params.map(mapParam), pl.restParam.map(mapParam))), varMap.toMap) + (params.map(pl => ParamList(pl.flags, pl.params.map(mapParam), pl.restParam.map(mapParam))(pl.toLoc)), varMap.toMap) def mkFieldReplacer(buf: VarSymbol, baseIdx: VarSymbol, symMap: Map[SimpleSymbol, SimpleSymbol]) = - def getOffset(off: Int)(k: Path => Block): Block = + def getOffset(off: Int, loc: Opt[Loc])(k: Path => Block): Block = val idxSymbol = new TempSymbol(N, erasedType = S(ErasedType.Int), "idx") - Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asSimpleRef, (baseIdx.asSimpleRef.asArg :: Value.Lit(Tree.IntLit(off)).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun), - k(DynSelect(buf.asSimpleRef.selSN("buf"), idxSymbol.asSimpleRef, true)))) + Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asSimpleRef, (baseIdx.asSimpleRef.asArg :: Value.Lit(Tree.IntLit(off))(N).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N), + k(DynSelect(buf.asSimpleRef.selSN("buf"), idxSymbol.asSimpleRef, true)(loc)))) def assignToOffset(off: Int, r: Result, rst: Block) = val idxSymbol = new TempSymbol(N, erasedType = S(ErasedType.Int), "idx") - Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asSimpleRef, (baseIdx.asSimpleRef.asArg :: Value.Lit(Tree.IntLit(off)).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun), + Scoped(Set.single(idxSymbol), Assign(idxSymbol, Call(State.builtinOpsMap("+").asSimpleRef, (baseIdx.asSimpleRef.asArg :: Value.Lit(Tree.IntLit(off))(N).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N), AssignDynField(buf.asSimpleRef.selSN("buf"), idxSymbol.asSimpleRef, true, r, applyBlock(rst)))) new BlockTransformer(SymbolSubst.Id): override def applySimpleSymbol(sym: SimpleSymbol): SimpleSymbol = symMap.getOrElse(sym, sym) @@ -66,11 +66,11 @@ class BufferableTransform()(using State, Raise): case sel: Select => sel.symbol.fold(super.applyPath(p)(k)): sym => fieldMap.get(sym).orElse(pubFieldMap.get(sym).flatMap(fieldMap.get(_))).fold(super.applyPath(p)(k)): off => - getOffset(off): res => + getOffset(off, p.toLoc): res => k(res) case r: Value.Ref => fieldMap.get(r.symbol).fold(super.applyPath(p)(k)): off => - getOffset(off): res => + getOffset(off, p.toLoc): res => k(res) case _ => super.applyPath(p)(k) def transformFunDefn(f: FunDefn, isCtor: Bool): FunDefn = @@ -79,7 +79,7 @@ class BufferableTransform()(using State, Raise): val (newParams, symMap) = mkSymbolReplacer(f.params) val blk = mkFieldReplacer(buf, idx, symMap).applyBlock(f.body) FunDefn(f.owner, f.sym, TermSymbol(f.dSym.k, f.dSym.owner, f.dSym.id, erasedType = N), PlainParamList( - Param.simple(buf) :: Param.simple(idx) :: Nil) :: newParams, + Param.simple(buf) :: Param.simple(idx) :: Nil)(N) :: newParams, if isCtor then Begin(blk, Return(idx.asSimpleRef)) else blk)(configOverride = f.configOverride, annotations = f.annotations) val fakeCtor = transformFunDefn(FunDefn.withFreshSymbol( S(companionSym), @@ -92,7 +92,7 @@ class BufferableTransform()(using State, Raise): fakeCtor :: cls.methods.map(transformFunDefn(_, false)), Nil, clsSizeSym -> clsSizeTermSym :: Nil, - Define(ValDefn(clsSizeTermSym, clsSizeSym, Value.Lit(Tree.IntLit(fields.size)))(N, Nil), End()), + Define(ValDefn(clsSizeTermSym, clsSizeSym, Value.Lit(Tree.IntLit(fields.size))(N))(N, Nil), End()), annotations = Nil, ) k: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ClassParamFlattener.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ClassParamFlattener.scala index caa313e9aa..20bc2f7942 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ClassParamFlattener.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ClassParamFlattener.scala @@ -30,14 +30,14 @@ class ClassParamFlattener(using State) extends BlockTransformer(SymbolSubst.Id): val flags = paramss.headOption.fold(ParamListFlags.empty)(_.flags) val (init, last) = paramss.splitAt(paramss.length - 1) val params = init.flatMap(_.allParams) ::: last.flatMap(_.params) - ParamList(flags, params, last.flatMap(_.restParam).headOption) + ParamList(flags, params, last.flatMap(_.restParam).headOption)(Loc(paramss)) /** Normalize class params so that `paramsOpt = N` and `auxParams` has exactly one element. */ private def flattenClsParams(cls: ClsLikeDefn): ClsLikeDefn = if cls.paramsOpt.isEmpty && cls.auxParams.sizeIs == 1 then return cls val paramss = cls.paramsOpt.toList ::: cls.auxParams val flatAux = paramss match - case Nil => PlainParamList(Nil) + case Nil => PlainParamList(Nil)(N) case single :: Nil => single case _ => flattenParamLists(paramss) cls.copy(paramsOpt = N, auxParams = flatAux :: Nil)( @@ -46,8 +46,8 @@ class ClassParamFlattener(using State) extends BlockTransformer(SymbolSubst.Id): ) private def classPathFor(fun: Path, cls: ClassSymbol): Opt[Path] = fun match - case Value.MemberRef(bms, _) => S(bms.asMemberRef(cls)) - case s @ Select(qual, name) => S(Select(qual, name)(S(cls))(s.sanitize)) + case Value.MemberRef(bms, _) => S(bms.asMemberRef(cls).withLoc(fun.toLoc)) + case s @ Select(qual, name) => S(Select(qual, name)(S(cls), s.toLoc)(s.sanitize)) case _ => N private def saturatedCurriedClassCall(fun: Path, argss: NELs[Ls[Arg]]): Opt[Path] = @@ -77,7 +77,7 @@ class ClassParamFlattener(using State) extends BlockTransformer(SymbolSubst.Id): else argss2 k: if flatArgss is argss then c - else Call(r, flatArgss)(c.metadata).withLocOf(c) + else Call(r, flatArgss)(c.metadata, c.toLoc) case call @ Call(fun, argss) => saturatedCurriedClassCall(fun, argss) match case S(cls) => @@ -86,7 +86,7 @@ class ClassParamFlattener(using State) extends BlockTransformer(SymbolSubst.Id): val flatArgss = if argss2.lengthCompare(1) > 0 then argss2.flatten ne_:: Nil else argss2 - k(Instantiate(false, cls2, flatArgss)(InstantiateMetadata(call.metadata.annotations)).withLocOf(call)) + k(Instantiate(false, cls2, flatArgss)(InstantiateMetadata(call.metadata.annotations), call.toLoc)) case N => super.applyResult(r)(k) case inst @ Instantiate(mut, cls, argss) => @@ -97,7 +97,7 @@ class ClassParamFlattener(using State) extends BlockTransformer(SymbolSubst.Id): else argss2 k: if (cls2 is cls) && (flatArgss is argss) then inst - else Instantiate(mut, cls2, flatArgss)(inst.metadata).withLocOf(inst) + else Instantiate(mut, cls2, flatArgss)(inst.metadata, inst.toLoc) case _ => super.applyResult(r)(k) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala index 8849ad1e4c..a2a6ae484f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala @@ -30,11 +30,11 @@ class FirstClassFunctionTransformer val args = params.params.map(_.sym.asSimpleRef.asArg) ::: params.restParam.toList.map(p => Arg(S(SpreadKind.Eager), p.sym.asSimpleRef)) val callDef = FunDefn.withFreshSymbol(Some(clsSym), new BlockMemberSymbol("call", Nil, true), params :: Nil, - Return(Call(p, args ne_:: Nil)(CallMetadata.defaultMlsFun)))(N, annotations = Nil) + Return(Call(p, args ne_:: Nil)(CallMetadata.defaultMlsFun, N)))(N, annotations = Nil) ClsLikeDefn(None, clsSym, defSym, None, syntax.Cls, None, Nil, - Some(Select(State.globalThisSymbol.asThis, Tree.Ident("Function"))(Some(ctx.builtins.Function))(false)), + Some(Select(State.globalThisSymbol.asThis, Tree.Ident("Function"))(Some(ctx.builtins.Function), N)(false)), callDef :: Nil, Nil, Nil, Assign.discard( - Call(State.builtinOpsMap("super").asSimpleRef, Nil ne_:: Nil)(CallMetadata.defaultFun), + Call(State.builtinOpsMap("super").asSimpleRef, Nil ne_:: Nil)(CallMetadata.defaultFun, N), End()), End(), None, None)(N, annotations = Nil) private def getParamList(l: BlockMemberSymbol): Option[ParamList] = funDefns.get(l) match @@ -63,7 +63,7 @@ class FirstClassFunctionTransformer val clsDef = generateFCFunctionClass(p, params) val tmp = new TempSymbol(None, erasedType = S(ErasedType.ValueLike(rsc = S(false), clsDef.isym.asClsOrMod.get))) val cls = clsDef.sym.asMemberRef(clsDef.isym) - Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil :: Nil)(InstantiateMetadata.empty), k(tmp.asSimpleRef)))) + Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil :: Nil)(InstantiateMetadata.empty, p.toLoc), k(tmp.asSimpleRef.withLoc(p.toLoc))))) override def applyPath(p: Path)(k: Path => Block): Block = p match case ref @ Value.MemberRef(l, disamb) => disamb match @@ -80,7 +80,7 @@ class FirstClassFunctionTransformer ErrorReport(msg"Cannot get ${s.nme}'s parameter list." -> sel.toLoc :: Nil, source = Diagnostic.Source.Compilation) - PlainParamList(Nil) + PlainParamList(Nil)(N) etaExpandPath(sel, params)(k) case Some(_) => k(p) case _ => @@ -94,7 +94,7 @@ class FirstClassFunctionTransformer def call(f: Path) = if (f is fun) && (argss is argss2) then c - else Call(f, argss2.ne_!)(c.metadata) + else Call(f, argss2.ne_!)(c.metadata, c.toLoc) fun match case ref @ Value.SimpleRef(sym) => sym match case _: VarSymbol | _: TempSymbol => k(call(ref.selSN("call"))) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala index 958d35d332..1e87748365 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/FlowAnalysisBasedRewrite.scala @@ -41,7 +41,7 @@ class FlowAnalysisBasedRewrite( case (param, i) => if eliminable(i) then removed.add(param.sym) else keptParams.append(param) - ParamList(pl.flags, keptParams.toList, pl.restParam) -> removed.toSet + ParamList(pl.flags, keptParams.toList, pl.restParam)(pl.toLoc) -> removed.toSet private def filterFunParams(funSym: TermSymbol, params: Ls[ParamList]): (Ls[ParamList], Set[VarSymbol]) = val removed = MutSet.empty[VarSymbol] @@ -94,7 +94,7 @@ class FlowAnalysisBasedRewrite( .toList val restParam = Option.when(targets.hasRestParam)(etaParam("rest")) EtaParamList( - ParamList(ParamListFlags.empty, params, restParam), + ParamList(ParamListFlags.empty, params, restParam)(N), params.map(p => Arg(N, p.sym.asSimpleRef)) ::: restParam.toList.map(p => Arg(S(SpreadKind.Eager), p.sym.asSimpleRef)), ) @@ -102,7 +102,7 @@ class FlowAnalysisBasedRewrite( lastWords("not the same shape?") private def etaCall(base: Path): Result = - Call(base, activeEtaArgss.ne_!)(CallMetadata.mlsFunWithEffect) + Call(base, activeEtaArgss.ne_!)(CallMetadata.mlsFunWithEffect, N) override def rewriteFunDefn(fun: FunDefn): RewrittenFunDefn = @@ -123,7 +123,7 @@ class FlowAnalysisBasedRewrite( override def applyValue(v: Value)(k: Value => Block): Block = v match case ref@Value.SimpleRef(l: VarSymbol) if activeEliminatedParams(l) => - k(Value.Lit(Tree.UnitLit(false)).withLocOf(ref)) + k(Value.Lit(Tree.UnitLit(false))(ref.toLoc)) case _ => super.applyValue(v)(k) override def applyBlock(b: Block): Block = b match @@ -138,7 +138,7 @@ class FlowAnalysisBasedRewrite( Return(etaCall(p).withLocOf(res2)) case c @ Call(fun, argss) => Return( - Call(fun, (argss ++ activeEtaArgss).ne_!)(c.metadata)) + Call(fun, (argss ++ activeEtaArgss).ne_!)(c.metadata, c.toLoc)) case _ => val tmp = TempSymbol(N, erasedType = N, "eta$res") Scoped( @@ -174,7 +174,7 @@ class FlowAnalysisBasedRewrite( if deadConstructorElimSolver.deadCtors.contains(ConcreteId(ctorSite.uid, instId)) => (args.reverseIterator.map(_.value) ++ selectedFrom) .filterNot(_.isPure) - .foldLeft(k(Value.Lit(Tree.UnitLit(false)).withLocOf(ctorSite))): (rest, p) => + .foldLeft(k(Value.Lit(Tree.UnitLit(false))(ctorSite.toLoc))): (rest, p) => applyPath(p)(Assign.discard(_, rest)) case c@Call(fun, args :: restArgss) if args.forall(_.spread.isEmpty) => val eliminable = deadParamElimSolver.eliminableCallSiteArgs(ConcreteId(c.uid, instId)) @@ -182,7 +182,7 @@ class FlowAnalysisBasedRewrite( rewriteArgs(args, eliminable): args2 => k( if (fun2 is fun) && (args2 is args) then c - else Call(fun2, args2 ne_:: restArgss)(c.metadata).withLocOf(c) + else Call(fun2, args2 ne_:: restArgss)(c.metadata, c.toLoc) ) case i@Instantiate(mut, cls, args :: restArgss) if args.forall(_.spread.isEmpty) => val eliminable = deadParamElimSolver.eliminableCallSiteArgs(ConcreteId(i.uid, instId)) @@ -190,7 +190,7 @@ class FlowAnalysisBasedRewrite( rewriteArgs(args, eliminable): args2 => k( if (cls2 is cls) && (args2 is args) then i - else Instantiate(mut, cls2, args2 :: restArgss)(i.metadata).withLocOf(i) + else Instantiate(mut, cls2, args2 :: restArgss)(i.metadata, i.toLoc) ) case _ => super.applyResult(r)(k) @@ -219,9 +219,9 @@ class FlowAnalysisBasedRewrite( withEtaArgss(etaParams.map(_.args)): applyFunBodyLikeBlock(lam.body) val wrappedBody = etaParams.map(_.params).foldRight(body2): (params, body) => - Return(Lambda(params, body)(Nil)) + Return(Lambda(params, body)(Nil, N)) if (params2 is lam.params) && (wrappedBody is lam.body) then lam - else Lambda(params2, wrappedBody)(lam.annot).withLocOf(lam) + else Lambda(params2, wrappedBody)(lam.annot, lam.toLoc) end Rewriter @@ -264,8 +264,8 @@ class FlowAnalysisBasedRewrite( refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness)(p.toLoc) val refreshedParams = rewrittenParams.map: - case ParamList(flags, params, restParam) => - ParamList(flags, params.map(refreshParam), restParam.map(refreshParam)) + case pl @ ParamList(flags, params, restParam) => + ParamList(flags, params.map(refreshParam), restParam.map(refreshParam))(pl.toLoc) FunDefn( N, bms, tSym, refreshedParams, new RefreshSymbol(refreshParamMap.toMap).apply(rewrittenBody))( diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala index cab9354767..1837096030 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/HandlerLowering.scala @@ -24,12 +24,12 @@ object HandlerLowering: private val nextIdent: Tree.Ident = Tree.Ident("next") private val lastIdent: Tree.Ident = Tree.Ident("last") private val contTraceIdent: Tree.Ident = Tree.Ident("contTrace") - private def unit = Value.Lit(Tree.UnitLit(true)) - private def intLit(i: BigInt) = Value.Lit(Tree.IntLit(i)) + private def unit = Value.Lit(Tree.UnitLit(true))(N) + private def intLit(i: BigInt) = Value.Lit(Tree.IntLit(i))(N) private def locToStr(loc: Loc) = val (line, _, col) = loc.origin.fph.getLineColAt(loc.spanStart) - Value.Lit(Tree.StrLit(s"${loc.origin.fileName.last}:${line + loc.origin.startLineNum - 1}:$col")) + Value.Lit(Tree.StrLit(s"${loc.origin.fileName.last}:${line + loc.origin.startLineNum - 1}:$col"))(N) extension (p: Path) def pc = p.selN(pcIdent) @@ -71,7 +71,7 @@ object HandlerLowering: resumeInfo.argLists ++: (intLit(restoreList.length) :: restoreList.map(_.asPath)) - ).map(_.asArg) ne_:: Nil)(CallMetadata.mlsFunWithEffect)) + ).map(_.asArg) ne_:: Nil)(CallMetadata.mlsFunWithEffect, N)) // argLists: length-encoded argument list used for resumption. // currentLocals: All locals to be saved and reloaded, this cannot include any variables in outer scopes @@ -131,7 +131,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R private def rtThrowMsg(msg: Str) = Throw.error(msg) object PureCall: - def apply(fun: Path, args: List[Path]) = Call(fun, args.map(Arg(N, _)) ne_:: Nil)(CallMetadata.defaultMlsFun) + def apply(fun: Path, args: List[Path]) = Call(fun, args.map(Arg(N, _)) ne_:: Nil)(CallMetadata.defaultMlsFun, N) def unapply(res: Result) = res match case Call(fun, args :: Nil) => args.foldRight[Opt[List[Path]]](S(Nil)): (arg, acc) => acc.flatMap: acc => @@ -144,7 +144,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R object StateTransition: private val transitionSymbol = freshTmp(erasedType = N, "transition") def apply(uid: StateId) = - Return(PureCall(transitionSymbol.asSimpleRef, List(Value.Lit(Tree.IntLit(uid))))) + Return(PureCall(transitionSymbol.asSimpleRef, List(Value.Lit(Tree.IntLit(uid))(N)))) def unapply(blk: Block) = blk match case Return(PureCall(Value.SimpleRef(`transitionSymbol`), List(Value.Lit(Tree.IntLit(uid))))) => S(uid) @@ -153,7 +153,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R object Unwind: private val unwindSymbol = freshTmp(erasedType = N, "unwind") def apply(uid: StateId, loc: Value) = - Return(PureCall(unwindSymbol.asSimpleRef, List(Value.Lit(Tree.IntLit(uid)), loc))) + Return(PureCall(unwindSymbol.asSimpleRef, List(Value.Lit(Tree.IntLit(uid))(N), loc))) def unapply(blk: Block) = blk match case Return(PureCall(Value.SimpleRef(`unwindSymbol`), List(Value.Lit(Tree.IntLit(uid)), loc: Value))) => S(uid, loc) @@ -537,9 +537,9 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val varList = scopedVars.collect: case sym: LocalVarSymbol => sym val sortedVars = varList.toList.sortBy(_.uid) - val debugInfo = Value.Lit(Tree.StrLit(debugNme)).asArg :: sortedVars.zipWithIndex.filter(_._1.isInstanceOf[VarSymbol]) + val debugInfo = Value.Lit(Tree.StrLit(debugNme))(N).asArg :: sortedVars.zipWithIndex.filter(_._1.isInstanceOf[VarSymbol]) .flatMap: (sym, idx) => - List(intLit(idx), Value.Lit(Tree.StrLit(sym.nme))) + List(intLit(idx), Value.Lit(Tree.StrLit(sym.nme))(N)) .map(_.asArg) val debugInfoSym = freshTmp(erasedType = S(ErasedType.Array), s"$debugNme$$debugInfo") // TODO: properly support spread argument by calculating the correct length. @@ -556,16 +556,16 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val preTransform = new BlockTransformer(SymbolSubst.Id): override def applyResult(r: Result)(k: Result => Block): Block = r match case Call(Value.MemberRef(sym, _), args) if sym is Elaborator.ctx.builtins.runtime.suspend => - k(Call(paths.mkEffectPath, args)(CallMetadata.mlsFunWithEffect)) + k(Call(paths.mkEffectPath, args)(CallMetadata.mlsFunWithEffect, N)) case Call(Value.MemberRef(sym, _), args) if sym is Elaborator.ctx.builtins.runtime.handle_suspension => - k(Call(paths.enterHandleBlockPath, args)(CallMetadata.mlsFunWithEffect)) + k(Call(paths.enterHandleBlockPath, args)(CallMetadata.mlsFunWithEffect, N)) case _ => super.applyResult(r)(k) override def applyDefn(defn: Defn)(k: Defn => Block): Block = defn match case fun: FunDefn => if h.currentBlockIsTrulyNested && opt.isDefined then raise(lifterReport(msg"Unexpected nested function: lambdas may not function correctly." -> fun.sym.toLoc :: Nil)) val (debugInfoSym, debugInfo, fun2) = translateFunLike(fun, fun.sym.asMemberRef(fun.dSym), N, fun.sym.nme) - if debugEnabled then Scoped(Set.single(debugInfoSym), Assign(debugInfoSym, Tuple(false, debugInfo), k(fun2))) else k(fun2) + if debugEnabled then Scoped(Set.single(debugInfoSym), Assign(debugInfoSym, Tuple(false, debugInfo)(N), k(fun2))) else k(fun2) case defn @ ClsLikeDefn(owner, isym, sym, ctorSym, kind, paramsOpt, auxParams, parentPath, methods, privateFields, publicFields, preCtor, ctor, companion, bufferable) => if h.currentBlockIsTrulyNested && opt.isDefined then raise(lifterReport(msg"Unexpected nested class: lambdas may not function correctly." -> isym.toLoc :: Nil)) @@ -602,7 +602,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R defn.copy(methods = newMtds, preCtor = newPreCtor, ctor = newCtor, companion = companion2)(defn.configOverride, defn.annotations) if debugEnabled then Scoped(debugInfos.map(_._1).toSet, debugInfos.foldRight(k(c2)): (elem, blk) => - Assign(elem._1, Tuple(false, elem._2), blk)) + Assign(elem._1, Tuple(false, elem._2)(N), blk)) else k(c2) case _ => super.applyDefn(defn)(k) val b = preTransform.applyBlock(blk) @@ -655,14 +655,14 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val transform = postTransform: uid => assert(uid === nextState) if isSimple then - Assign(pcVar, Value.Lit(Tree.IntLit(uid)), End()) + Assign(pcVar, Value.Lit(Tree.IntLit(uid))(N), End()) else Break(lblSym) val transformed = transform.applyBlock(blk.blk) if isSimple then transformed else Label( lblSym, false, transformed, - Assign(pcVar, Value.Lit(Tree.IntLit(nextState)), End()) + Assign(pcVar, Value.Lit(Tree.IntLit(nextState))(N), End()) ) line match case head :: next => @@ -701,18 +701,18 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val getSavedTmp = freshTmp(erasedType = S(ErasedType.Int), "saveOffset") def getSaved(off: BigInt): (Block => Block, Path) = if off == 0 then - return (id, DynSelect(paths.runtimePath.selSN("resumeArr"), paths.runtimePath.selSN("resumeIdx"), true)) - val addOne = Assign(getSavedTmp, Call(State.builtinOpsMap("+").asSimpleRef, (paths.runtimePath.selSN("resumeIdx").asArg :: intLit(off).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun), _) - (addOne, DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asSimpleRef, true)) + return (id, DynSelect(paths.runtimePath.selSN("resumeArr"), paths.runtimePath.selSN("resumeIdx"), true)(N)) + val addOne = Assign(getSavedTmp, Call(State.builtinOpsMap("+").asSimpleRef, (paths.runtimePath.selSN("resumeIdx").asArg :: intLit(off).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun, N), _) + (addOne, DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asSimpleRef, true)(N)) - val resumeArrIndexed = DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asSimpleRef, true) + val resumeArrIndexed = DynSelect(paths.runtimePath.selSN("resumeArr"), getSavedTmp.asSimpleRef, true)(N) val plus = State.builtinOpsMap("+").asSimpleRef val preRestore = blockBuilder .assign(pcVar, paths.resumePc) .scopedVars(Set(getSavedTmp)) val restoreVars = vars.zipWithIndex.foldLeft(preRestore): case (builder, (local, idx)) => builder - .assign(getSavedTmp, if idx == 0 then paths.resumeIdx else Call(plus, (getSavedTmp.asSimpleRef.asArg :: intLit(1).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun)) + .assign(getSavedTmp, if idx == 0 then paths.resumeIdx else Call(plus, (getSavedTmp.asSimpleRef.asArg :: intLit(1).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun, N)) .assign(local, resumeArrIndexed) if needsStackSafety then @@ -721,7 +721,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R .ifthen(paths.curEffect, Case.Lit(Tree.UnitLit(true)), End(), S( ctx.doUnwind(ctx.resumeInfo.currentStackSafetySym.fold(_.toLoc, _.toLoc).fold(unit)(locToStr(_)), if oneState then intLit(-1) else pcVar.asSimpleRef, vars)(using paths))) - .assign(curDepth, Call(plus, (paths.stackDepthPath.asArg :: intLit(1).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun)) + .assign(curDepth, Call(plus, (paths.stackDepthPath.asArg :: intLit(1).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultFun, N)) .rest(mainBody) if !oneState then @@ -747,10 +747,10 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R */ private def postTranslateTopLevelCtx(b: Block)(using HandlerCtx): Block = - postTranslateIllegalEffectCtx(b, Call.raw(paths.topLevelEffectPath, (Value.Lit(Tree.BoolLit(debugEnabled)).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun), stackSafety.map(_.stackLimit)) + postTranslateIllegalEffectCtx(b, Call.raw(paths.topLevelEffectPath, (Value.Lit(Tree.BoolLit(debugEnabled))(N).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N), stackSafety.map(_.stackLimit)) private def postTranslateIllegalEffectCtx(b: Block, reason: Str)(using HandlerCtx): Block = - postTranslateIllegalEffectCtx(b, Call.raw(paths.illegalEffectPath, (Value.Lit(Tree.StrLit(reason)).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun), N) + postTranslateIllegalEffectCtx(b, Call.raw(paths.illegalEffectPath, (Value.Lit(Tree.StrLit(reason))(N).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N), N) /** * Translate the block and apply stack safety wrapper if needed. If needsStackSafety is true, @@ -761,11 +761,11 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val withStackSafe = needsStackSafety match case S(stackLimit) => val bodSym = BlockMemberSymbol("‹stack safe body›", Nil, false) - val bodFun = FunDefn.withFreshSymbol(N, bodSym, ParamList(ParamListFlags.empty, Nil, N) :: Nil, Ret(r))(configOverride = N, annotations = Nil) + val bodFun = FunDefn.withFreshSymbol(N, bodSym, ParamList(ParamListFlags.empty, Nil, N)(N) :: Nil, Ret(r))(configOverride = N, annotations = Nil) blockBuilder .scopedVars(Set.single(bodSym)) .define(bodFun) - .assign(l, Call(paths.runStackSafePath, (intLit(stackLimit).asArg :: Value.MemberRef(bodSym, bodFun.dSym).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun)) + .assign(l, Call(paths.runStackSafePath, (intLit(stackLimit).asArg :: Value.MemberRef(bodSym, bodFun.dSym)(N).asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N)) case N => blockBuilder.assign(l, r) withStackSafe @@ -795,7 +795,7 @@ class HandlerLowering(paths: HandlerPaths, opt: Opt[EffectHandlers])(using TL, R val transformed = blockBuilder .staticif( opt.fold(false)(!_.doNotInstrumentTopLevelModCtor), - _.assign(NoSymbol, Call(paths.resetEffects, Nil ne_:: Nil)(CallMetadata.defaultMlsFun)) + _.assign(NoSymbol, Call(paths.resetEffects, Nil ne_:: Nil)(CallMetadata.defaultMlsFun, N)) ) .rest(translateBlock(prog.main, ctx, Set.empty)) if transformed is prog.main then prog diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala index 9cbffaa976..9bddf3a37d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lifter.scala @@ -125,7 +125,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case Sym(l) => l.asSimpleRef case ThisPath(sym) => sym.asThis case BmsRef(l, d) => l.asMemberRef(d) - case Field(path, field) => Select(path, field.id)(S(field))(false) + case Field(path, field) => Select(path, field.id)(S(field), N)(false) def asArg(using ctx: LifterCtxNew) = read.asArg @@ -162,7 +162,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): case Sym(l) => l.asSimpleRef case PathRef(path) => path case InScope(l, d) => l.asMemberRef(d) - case Field(isym, l, d) => Select(ctx.symbolsMap(isym).read, Tree.Ident(l.nme))(S(d))(false) + case Field(isym, l, d) => Select(ctx.symbolsMap(isym).read, Tree.Ident(l.nme))(S(d), N)(false) def asArg(using ctx: LifterCtxNew) = read.asArg @@ -368,11 +368,11 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): def join2: Block = // Resolve reference to unlifted object resolveDefnRef(d, r) match - case Some(value) => k(c.copy(fun = value, argss = newArgss.ne_!)(c.metadata).withLoc(c.toLoc)) + case Some(value) => k(c.copy(fun = value, argss = newArgss.ne_!)(c.metadata, c.toLoc)) case None => super.applyPath(c.fun): fun2 => // Nothing to rewrite if (fun2 is c.fun) && (argss is newArgss) then k(c) - else k(c.copy(fun = fun2, argss = newArgss.ne_!)(c.metadata).withLoc(c.toLoc)) + else k(c.copy(fun = fun2, argss = newArgss.ne_!)(c.metadata, c.toLoc)) r match // Call to lifted function: Rewrite using the efficient version case f: LiftedFunc => k(f.rewriteCall(c, newArgss)) @@ -386,12 +386,12 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): applyArgss(argss): newArgss => def join = if argss is newArgss then inst - else inst.copy(argss = newArgss)(inst.metadata).withLoc(inst.toLoc) + else inst.copy(argss = newArgss)(inst.metadata, inst.toLoc) ctx.rewrittenScopes.get(d) match case N => k(join) case S(c: LiftedClass) => c.rewriteInstantiate(inst, newArgss)(k) case S(r) => resolveDefnRef(d, r) match - case Some(value) => k(Instantiate(inst.mut, value, newArgss)(inst.metadata).withLoc(inst.toLoc)) + case Some(value) => k(Instantiate(inst.mut, value, newArgss)(inst.metadata, inst.toLoc)) case None => k(join) case _ => super.applyResult(r)(k) @@ -598,7 +598,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): S(ClassCtorSymbol(syntax.Fun, N, clsSym)), syntax.Cls, N, - PlainParamList(sortedVars.iterator.map(_.param).toList) :: Nil, None, Nil, Nil, + PlainParamList(sortedVars.iterator.map(_.param).toList)(N) :: Nil, None, Nil, Nil, Nil, End(), sortedVars.iterator.foldLeft[Block](End()): @@ -689,7 +689,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): captureClass.sym.asMemberRef(captureClass.isym), captureInfo._2.map( (sym, _) => sym.asPath.asArg) :: Nil - )(InstantiateMetadata.empty) + )(InstantiateMetadata.empty, N) else lastWords("tried to instantiate an empty capture") protected final def addExtraSyms(b: Block, captureSym: => LocalVarSymbol, objSyms: Iterable[ScopedSymbol]): Block = @@ -885,7 +885,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): */ sealed trait ClsLikeRewrittenScope[T](sym: InnerSymbol) extends RewrittenScope[T]: lazy val captureSym = TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(obj.nme + "$cap"), erasedType = captureType) - override lazy val capturePath = Select(sym.asThis, captureSym.id)(S(captureSym))(false) + override lazy val capturePath = Select(sym.asThis, captureSym.id)(S(captureSym), N)(false) protected val liftedObjsOrdered: List[InnerSymbol] = node.liftedObjSyms.toList.sortBy(_.uid) protected val liftedObjsSyms: Map[InnerSymbol, TermSymbol] = liftedObjsOrdered.map: s => s -> TermSymbol(syntax.ImmutVal, S(sym), Tree.Ident(s.nme + "$"), erasedType = N) @@ -912,7 +912,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): private def dupParam(p: Param): Param = p.copy(sym = VarSymbol(Tree.Ident(p.sym.nme), erasedType = p.sym.erasedType))(p.toLoc) private def dupParams(plist: List[Param]): List[Param] = plist.map(dupParam) private def dupParamList(plist: ParamList): ParamList = - plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam)) + plist.copy(params = dupParams(plist.params), restParam = plist.restParam.map(dupParam))(plist.toLoc) /* CONCRETE IMPLS */ @@ -968,7 +968,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap"), erasedType = N) - override lazy val capturePath: Path = Select(obj.cls.isym.asThis, captureSym.id)(S(captureSym))(false) + override lazy val capturePath: Path = Select(obj.cls.isym.asThis, captureSym.id)(S(captureSym), N)(false) override def rewriteImpl: LifterResult[ClsLikeDefn] = val liftedSuper = obj.cls.parentPath.flatMap: @@ -1002,7 +1002,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): with ClsLikeRewrittenScope[ClsLikeBody](obj.clsBody.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.clsBody.isym), Tree.Ident(obj.nme + "$cap"), erasedType = N) - override lazy val capturePath: Path = Select(obj.clsBody.isym.asThis, captureSym.id)(S(captureSym))(false) + override lazy val capturePath: Path = Select(obj.clsBody.isym.asThis, captureSym.id)(S(captureSym), N)(false) override def rewriteImpl: LifterResult[ClsLikeBody] = val rewriterCtor = new BlockRewriter(N) @@ -1059,20 +1059,20 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): // Definition with the auxiliary parameters merged into the first parameter list. private def mkFlattenedDefn: LifterResult[FunDefn] = val newPlists = fun.params match - case head :: next => head.copy(params = auxParams ::: head.params) :: next - case Nil => PlainParamList(auxParams) :: Nil + case head :: next => head.copy(params = auxParams ::: head.params)(head.toLoc) :: next + case Nil => PlainParamList(auxParams)(N) :: Nil val rewriter = new BlockRewriter(N) val newBod = rewriter.rewrite(fun.body) val withCapture = addExtraSyms(newBod) val newDefn = fun.copy(owner = N, sym = mainSym, dSym = mainDsym, params = newPlists, body = withCapture)( fun.configOverride, - if liftedFromStagedModule && !fun.isStaged then Annot.Modifier(Keyword.`staged`) :: fun.annotations + if liftedFromStagedModule && !fun.isStaged then Annot.Modifier(Keyword.`staged`)(N) :: fun.annotations else fun.annotations) LifterResult(newDefn, rewriter.extraDefns.toList) // Definition with the auxiliary parameters as a new first parameter list. private def mkAuxDefn: FunDefn = - val newPList = PlainParamList(dupParams(auxParams)) + val newPList = PlainParamList(dupParams(auxParams))(N) val (newPlists, syms, restSym) = fun.params match case head :: _ => val duped = dupParamList(head) @@ -1086,7 +1086,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): syms.map(sym => Arg(N, sym.asSimpleRef)) ::: Arg(S(SpreadKind.Eager), value.asSimpleRef) :: Nil case None => syms.map(s => Arg(N, s.asSimpleRef)) - val call = Call(fun.sym.asMemberRef(fun.dSym), args ne_:: Nil)(CallMetadata.mlsFunWithEffect) + val call = Call(fun.sym.asMemberRef(fun.dSym), args ne_:: Nil)(CallMetadata.mlsFunWithEffect, N) val bod = Return(call) FunDefn( @@ -1095,19 +1095,19 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): auxDsym, newPlists, bod - )(N, if fun.noInline then fun.annotations else Annot.Inline :: fun.annotations) + )(N, if fun.noInline then fun.annotations else Annot.Inline()(N) :: fun.annotations) private val aux = Lazy[Defn](mkAuxDefn) def rewriteCall(c: Call, argss: NELs[List[Arg]])(using ctx: LifterCtxNew): Call = if isTrivial then if argss is c.argss then c - else c.copy(argss = argss)(c.metadata).withLocOf(c) + else c.copy(argss = argss)(c.metadata, c.toLoc) else Call.raw( mainSym.asMemberRef(mainDsym), (formatArgs ::: argss.head) ne_:: argss.tail - )(c.metadata.copy(isMlsFun = true)).withLoc(c.toLoc) + )(c.metadata.copy(isMlsFun = true), c.toLoc) def rewriteRef(using ctx: LifterCtxNew): Call = if isTrivial then lastWords("tried to rewrite a ref to a trivial function") @@ -1115,7 +1115,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): Call.raw( auxSym.asMemberRef(auxDsym), formatArgs ne_:: Nil - )(CallMetadata.defaultMlsFun) + )(CallMetadata.defaultMlsFun, N) def rewriteImpl: LifterResult[FunDefn] = val LifterResult(lifted, extra) = mkFlattenedDefn @@ -1126,7 +1126,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): with ClsLikeRewrittenScope[ClsLikeDefn](obj.cls.isym): private val captureSym = TermSymbol(syntax.ImmutVal, S(obj.cls.isym), Tree.Ident(obj.nme + "$cap"), erasedType = N) - override lazy val capturePath: Path = Select(obj.cls.isym.asThis, captureSym.id)(S(captureSym))(false) + override lazy val capturePath: Path = Select(obj.cls.isym.asThis, captureSym.id)(S(captureSym), N)(false) private val passedSymsMap_ : Map[ValueSymbol, (vs: VarSymbol, ts: TermSymbol)] = passedSymsOrdered.map: s => val erasedType = s.mapErasedValueType @@ -1173,7 +1173,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): ::: capturesOrdered.map(x => capSymsMap_(x).vs) ::: passedSymsOrdered.map(x => passedSymsMap_(x).vs)) .map(Param.simple(_)) - lazy val auxParamList = PlainParamList(auxParams) + lazy val auxParamList = PlainParamList(auxParams)(N) // Whether this can be lifted without the need to pass extra parameters. lazy val isTrivial = auxParams.isEmpty @@ -1187,7 +1187,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): def mkFlattenedDefn: FunDefn = // Symbols for the aux parameter list val auxSyms = auxParams.map(p => VarSymbol(Tree.Ident(p.sym.nme), erasedType = p.sym.erasedType)) - val auxParamListLocal = PlainParamList(auxSyms.map(Param.simple(_))) + val auxParamListLocal = PlainParamList(auxSyms.map(Param.simple(_)))(N) val dupedClsAuxParams = cls.auxParams.map(dupParamList(_)) val dupedMainOpt = cls.paramsOpt.map(dupParamList(_)) @@ -1224,14 +1224,14 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): val argsList = appliedMainAndAuxArgs(appliedClsAuxArgs) val ref = obj.cls.sym.asMemberRef(obj.cls.isym) - val inst = Instantiate(false, ref, argsList)(InstantiateMetadata.empty) + val inst = Instantiate(false, ref, argsList)(InstantiateMetadata.empty, N) val bod = Return(inst) FunDefn(N, flattenedSym, flattenedDSym, allParamLists, bod)(N, annotations = Nil) private val flat = Lazy[Defn](mkFlattenedDefn) - def instObject = Instantiate(false, cls.sym.asMemberRef(cls.isym), formatArgs :: Nil)(InstantiateMetadata.empty) + def instObject = Instantiate(false, cls.sym.asMemberRef(cls.isym), formatArgs :: Nil)(InstantiateMetadata.empty, N) // Rewrite a naked reference to a parameterized class constructor. // Returns a Call to the curried C$ wrapper partially applied with formatArgs. @@ -1241,30 +1241,30 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): Call.raw( flattenedSym.asMemberRef(flattenedDSym), formatArgs ne_:: Nil - )(CallMetadata.defaultMlsFun) + )(CallMetadata.defaultMlsFun, N) def rewriteInstantiate(inst: Instantiate, argss: List[List[Arg]])(k: Result => Block): Block = if obj.isObj then lastWords("tried to rewrite instantiate for an object") val path = cls.sym.asMemberRef(cls.isym) if isTrivial then if (inst.cls === path) && (inst.argss is argss) then k(inst) - else k(inst.copy(cls = path, argss = argss)(inst.metadata).withLocOf(inst)) + else k(inst.copy(cls = path, argss = argss)(inst.metadata, inst.toLoc)) else if cls.paramsOpt.isEmpty && cls.auxParams.isEmpty then // Paramless class: lifter args go directly into the Instantiate constructor - k(Instantiate(inst.mut, path, (formatArgs ::: argss.head) :: argss.tail)(inst.metadata).withLoc(inst.toLoc)) + k(Instantiate(inst.mut, path, (formatArgs ::: argss.head) :: argss.tail)(inst.metadata, inst.toLoc)) else // Parameterized class: use Instantiate with original args + lifter args inserted after the first list - k(Instantiate(inst.mut, path, argss.head :: formatArgs :: argss.tail)(inst.metadata).withLoc(inst.toLoc)) + k(Instantiate(inst.mut, path, argss.head :: formatArgs :: argss.tail)(inst.metadata, inst.toLoc)) def rewriteSuperCall(superCall: Call, argss: List[List[Arg]])(k: Result => Block): Block = if obj.isObj then lastWords("tried to rewrite instantiate for an object") if isTrivial then k(superCall) else if cls.paramsOpt.isEmpty && cls.auxParams.isEmpty then // Paramless class: lifter args go directly into the Instantiate constructor - k(Call(superCall.fun, (formatArgs ::: argss.head) ne_:: argss.tail)(CallMetadata.defaultMlsFun).withLoc(superCall.toLoc)) + k(Call(superCall.fun, (formatArgs ::: argss.head) ne_:: argss.tail)(CallMetadata.defaultMlsFun, superCall.toLoc)) else // Parameterized class: use Instantiate with original args + lifter args inserted after the first list - k(Call(superCall.fun, argss.head ne_:: formatArgs ne_:: argss.tail)(CallMetadata.mlsFunWithEffect).withLoc(superCall.toLoc)) + k(Call(superCall.fun, argss.head ne_:: formatArgs ne_:: argss.tail)(CallMetadata.mlsFunWithEffect, superCall.toLoc)) def rewriteCall(c: Call, argss: NELs[List[Arg]])(k: Result => Block)(using ctx: LifterCtxNew): Block = if obj.isObj then lastWords("tried to rewrite instantiate for an object") @@ -1275,16 +1275,16 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): Call.raw( flattenedSym.asMemberRef(flattenedDSym), (formatArgs :: argss).ne_! - )(c.metadata.copy(isMlsFun = true, mayRaiseEffects = false)).withLoc(c.toLoc) + )(c.metadata.copy(isMlsFun = true, mayRaiseEffects = false), c.toLoc) if isTrivial then if c.argss is argss then k(c) - else k(c.copy(argss = argss)(c.metadata).withLocOf(c)) + else k(c.copy(argss = argss)(c.metadata, c.toLoc)) else if cls.paramsOpt.isEmpty && cls.auxParams.isEmpty then // Paramless class: unreachable lastWords("Call to paramless class") else if argss.lengthCompare(clsParamLists.length) === 0 then // Parameterized class: Same as Instantiate case - k(Instantiate(false, path, argss.head :: formatArgs :: argss.tail)(InstantiateMetadata(c.metadata.annotations)).withLoc(c.toLoc)) + k(Instantiate(false, path, argss.head :: formatArgs :: argss.tail)(InstantiateMetadata(c.metadata.annotations), c.toLoc)) else // Unsaturated constructor calls must remain ordinary curried calls to // the lifted wrapper; only saturated constructor applications may @@ -1352,7 +1352,7 @@ class Lifter(topLevelBlk: Block)(using State, Raise, Config): methods = newMtds, auxParams = newAuxList )(obj.cls.configOverride, - if liftedFromStagedModule && !obj.cls.isStaged then Annot.Modifier(Keyword.`staged`) :: obj.cls.annotations + if liftedFromStagedModule && !obj.cls.isStaged then Annot.Modifier(Keyword.`staged`)(N) :: obj.cls.annotations else obj.cls.annotations) val extrasDefns = rewriterCtor.extraDefns.toList ::: rewriterPreCtor.extraDefns.toList ::: extras LifterResult(newCls, flat :: extrasDefns) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 2d70936c9f..ded1cae497 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -76,7 +76,7 @@ import LoweringCtx.loweringCtx object Lowering: def compError: Block = - Throw(Value.Lit(Tree.StrLit("This code cannot be run as its compilation yielded an error."))) + Throw(Value.Lit(Tree.StrLit("This code cannot be run as its compilation yielded an error."))(N)) def fail(err: ErrorReport)(using Raise): Block = raise(err) @@ -155,10 +155,10 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): ) ++ blt.wasm.instrIntrinsics.map(_ -> SpecialBuiltin.WasmIntrinsic) lazy val unreachableFn = - Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("unreachable"))(S(State.unreachableSymbol))(false) + Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("unreachable"))(S(State.unreachableSymbol), N)(false) def unit: Path = - Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("Unit"))(S(State.unitSymbol))(false) + Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("Unit"))(S(State.unitSymbol), N)(false) private def memberIdent(nme: Tree.Ident, sym: Opt[MemberSymbol]): Tree.Ident = sym match @@ -175,7 +175,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): new Tree.Ident(sym.nme).withLocOf(nme) // type Rcd = (mut: Bool, args: List[RcdArg]) // * Better, but Scala's patmat exhaustiveness chokes on it - type Rcd = (Bool, List[RcdArg]) + type Rcd = (Bool, List[RcdArg], Opt[Loc]) /** Lowers `t` in tail-return position, coercing the result to the enclosing function's declared return type. */ def returnedTerm(t: st, returnType: Opt[ErasedType])(using LoweringCtx): Block = @@ -225,21 +225,21 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case Nil => res match case R(res) => term(res, inStmtPos = inStmtPos)(k) - case L((mut, flds)) => - k(Record(mut, flds.reverse)) + case L((mut, flds, loc)) => + k(Record(mut, flds.reverse)(loc)) case RcdSpread(bod) :: stats => res match case R(_) => wat("RcdField in non-Rcd context", res) - case L((mut, flds)) => + case L((mut, flds, loc)) => subTerm(bod): l => - blockImpl(stats, L((mut, RcdArg(N, l) :: flds))) + blockImpl(stats, L((mut, RcdArg(N, l) :: flds, loc))) case RcdField(lhs, rhs) :: stats => res match case R(_) => wat("RcdField in non-Rcd context", res) - case L((mut, flds)) => + case L((mut, flds, loc)) => subTerm(lhs): l => subTerm_nonTail(rhs): r => - blockImpl(stats, L((mut, RcdArg(S(l), r) :: flds))) + blockImpl(stats, L((mut, RcdArg(S(l), r) :: flds, loc))) case (decl @ LetDecl(sym, annotations)) :: stats => reportAnnotations(decl, annotations) if sym.asTrm.forall(_.owner.isEmpty) then @@ -311,7 +311,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case S(comp) => comp.defn.getOrElse(wat("Module companion without definition", mod.companion)) case N => val stagedAnnots = mod.annotations.collect: - case Annot.Modifier(Keyword.`staged`) => Annot.Modifier(Keyword.`staged`) + case a @ Annot.Modifier(Keyword.`staged`) => a ClassDef.Plain(mod.owner, syntax.Cls, new ClassSymbol(Tree.DummyTypeDef(syntax.Cls), mod.sym.id), mod.bsym, Nil, @@ -444,7 +444,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): raise(ErrorReport( msg"Extending a partially applied class is not supported" -> loc :: Nil, source = Diagnostic.Source.Compilation)) - k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, true, Nil)).withLoc(loc)) + k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, true, Nil), loc)) zipArgs(ctorParamLists, args, Nil) case Nil => if !ctorParamLists.isEmpty then @@ -452,7 +452,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"Extending a partially applied class is not supported" -> loc :: Nil, source = Diagnostic.Source.Compilation)) // * No arguments to a super ctor means a nullary call, e.g., `extends C` means `extends C()` - k(Call(fr, Nil ne_:: Nil)(CallMetadata(isMlsFun, true, Nil)).withLoc(loc)) + k(Call(fr, Nil ne_:: Nil)(CallMetadata(isMlsFun, true, Nil), loc)) /** Lower a call with multiple argument lists into `Call` nodes, * trying to group as many as possible into a single one @@ -463,16 +463,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case (ps :: remainingParams, args :: remainingArgs) => lowerArgs(args, expectedParamTypes(ps))(as => zipArgs(remainingParams, remainingArgs, as :: acc, mayRaiseEffects)) case (Nil, Nil) => - k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, mayRaiseEffects, annotations)).withLoc(loc)) + k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, mayRaiseEffects, annotations), loc)) case (Nil, args :: remainingArgss) => acc.reverse match case Nil => lowerRemainingCalls(fr, args, remainingArgss, annotations, loc)(k) case acc: NELs[Ls[Arg]] => - val call = Call(fr, acc)(CallMetadata(isMlsFun, mayRaiseEffects, Nil)).withLoc(loc) + val call = Call(fr, acc)(CallMetadata(isMlsFun, mayRaiseEffects, Nil), loc) val tmp = loweringCtx.registerTempSymbol(N, erasedType = call.erasedValueType, "baseCall") Assign(tmp, call, lowerRemainingCalls(tmp.asSimpleRef, args, remainingArgss, annotations, loc)(k)) case (_ :: _, Nil) => - k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, mayRaiseEffects, annotations)).withLoc(loc)) + k(Call(fr, acc.reverse.ne_!)(CallMetadata(isMlsFun, mayRaiseEffects, annotations), loc)) fr.targetSymbol match case S(fs: TermSymbol) => fs.defn match @@ -484,7 +484,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): def lowerRemainingCalls(base: Path, args: Term, remainingArgss: Ls[Term], annotations: Ls[Annot], loc: Opt[Loc]) (k: Result => Block)(using LoweringCtx): Block = lowerArgs(args, Nil): as => - val call = Call(base, as ne_:: Nil)(CallMetadata(false, true, annotations)).withLoc(loc) + val call = Call(base, as ne_:: Nil)(CallMetadata(false, true, annotations), loc) remainingArgss match case Nil => k(call) case args :: remainingArgss => @@ -497,10 +497,10 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): * when they correspond to constructor parameter lists of the same class. * If fewer argument lists are provided than constructor parameter lists, eta-expands * the missing ones with fresh lambdas (avoiding reliance on mutable JS class curry semantics). */ - def lowerMultiInstantiate(mut: Bool, cls: Path, args: Ls[Term], annotations: Ls[Annot])(k: Result => Block)(using LoweringCtx): Block = + def lowerMultiInstantiate(mut: Bool, cls: Path, args: Ls[Term], annotations: Ls[Annot], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = // Nullary instantiations are represented with one empty argument list, matching existing `Instantiate` usage. def buildInstantiate(argss: Ls[Ls[Arg]]): Instantiate = - Instantiate(mut, cls, if argss.isEmpty then Nil :: Nil else argss)(InstantiateMetadata(annotations)) + Instantiate(mut, cls, if argss.isEmpty then Nil :: Nil else argss)(InstantiateMetadata(annotations), loc) // * Zip constructor param lists with argument lists, accumulating lowered args. // * Consumes one argument list per constructor param list; when all ctor params are // * consumed but extra args remain, falls back to `Call` nodes on the result. @@ -526,9 +526,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val freshSyms = ps.params.map(p => new VarSymbol(new Tree.Ident(p.sym.nme), erasedType = N)) softTODO(ps.restParam.isEmpty, "Eta expanding rest parameters in constructor definitions is not yet supported") val freshParams = (ps.params zip freshSyms).map((p, s) => Param(p.flags, s, N, p.modulefulness)(p.toLoc)) - val freshParamList = ParamList(ps.flags, freshParams, N) + val freshParamList = ParamList(ps.flags, freshParams, N)(ps.toLoc) val freshArgs = freshSyms.map(s => Arg(N, s.asSimpleRef)) - Lambda(freshParamList, Return(etaExpand(rest, accArgss :+ freshArgs)))(Nil) + Lambda(freshParamList, Return(etaExpand(rest, accArgss :+ freshArgs)))(Nil, loc) k(etaExpand(remainingParamss, acc.reverse)) // * Resolve the class definition to get the constructor param lists. // * The class path typically resolves to a TermSymbol (the constructor function), @@ -637,7 +637,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val t2 = new Tree.Ident("arg2") val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none)(t1.toLoc) val p2 = Param(FldFlags.empty, VarSymbol(t2, erasedType = N), N, Modulefulness.none)(t2.toLoc) - val ps = PlainParamList(p1 :: p2 :: Nil) + val ps = PlainParamList(p1 :: p2 :: Nil)(N) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, N).resolve, st.Ref(p2.sym)(t2, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) )))( @@ -648,11 +648,11 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val (paramLists, bodyBlock) = setupFunctionDef(ps :: Nil, bod, S(sym.nme), N) tl.log(s"Ref builtin $sym") assert(paramLists.length === 1) - return k(Lambda(paramLists.head, bodyBlock)(Nil).withLocOf(ref)) + return k(Lambda(paramLists.head, bodyBlock)(Nil, ref.toLoc)) if sym.unary then val t1 = new Tree.Ident("arg") val p1 = Param(FldFlags.empty, VarSymbol(t1, erasedType = N), N, Modulefulness.none)(t1.toLoc) - val ps = PlainParamList(p1 :: Nil) + val ps = PlainParamList(p1 :: Nil)(N) val bod = st.App(ref, st.Tup(List(st.Ref(p1.sym)(t1, N).resolve)) (Tree.Tup(Nil // FIXME should not be required (using dummy value) )))( @@ -663,7 +663,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val (paramLists, bodyBlock) = setupFunctionDef(ps :: Nil, bod, S(sym.nme), N) tl.log(s"Ref builtin $sym") assert(paramLists.length === 1) - return k(Lambda(paramLists.head, bodyBlock)(Nil).withLocOf(ref)) + return k(Lambda(paramLists.head, bodyBlock)(Nil, ref.toLoc)) case bs: BlockMemberSymbol => disamb.flatMap(_.defn) match case S(d) if d.hasDeclareModifier.isDefined => @@ -679,14 +679,14 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): if isImplicitNullaryCall(td.tsym) then return k(Call( bs.asMemberRef(disamb.get).withLocOf(ref), Nil ne_:: Nil - )(CallMetadata(isMlsFun = true, mayRaiseEffects = true, annots))) + )(CallMetadata(isMlsFun = true, mayRaiseEffects = true, annots), ref.toLoc)) case S(td: TermDefinition) => td.tsym.owner match case S(owner) => // * With the current Elaborator semantics, selections are already inserted for most things; // * the current case can only happen if `td` is a let binding defined in some object owner. softAssert(td.k is syntax.LetBind, s"Expected a let binding, got a ${td.k.str} ($td)") - return k(Select(owner.asThis, td.tsym.id)(S(td.tsym))(false).withLocOf(ref)) + return k(Select(owner.asThis, td.tsym.id)(S(td.tsym), ref.toLoc)(false)) case N => () case S(_) => () case N => () // TODO panic here; can only lower refs to elab'd symbols @@ -694,8 +694,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): sym.owner match case S(owner) => warnStmt - val sel = Select(owner.asThis, sym.id)(S(sym))(false) - return k(sel.withLocOf(ref)) + val sel = Select(owner.asThis, sym.id)(S(sym), ref.toLoc)(false) + return k(sel) case N => () case _ => () warnStmt @@ -719,7 +719,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): @tailrec def extractAnnots(t: st, acc: List[Annot]): (List[Annot], st) = t match - case st.Annotated(Annot.Async, trm) => + case st.Annotated(Annot.Async(), trm) => val ident = new Tree.Ident("asyncBody").withLocOf(trm) val bms = BlockMemberSymbol(ident.name, Nil, false) val dsym = TermSymbol( @@ -732,13 +732,13 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): syntax.Fun, bms, dsym, - sem.ParamList(sem.ParamListFlags.empty, Nil, N) :: Nil, + sem.ParamList(sem.ParamListFlags.empty, Nil, N)(N) :: Nil, N, N, S(trm), TermDefFlags.empty, Modulefulness.none, - Annot.RaiseEffects :: Nil, + Annot.RaiseEffects()(N) :: Nil, N, )(trm.toLoc) val rewritten = st.App( @@ -755,8 +755,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // * `@untyped` discards the term's declared type by binding its value to a local declared with `Unknown`. // * Done here rather than on the IR node so that it reaches every shape of term, not just annotatable ones. - val isUntyped = allAnnots.contains(Annot.Untyped) - val annots = if isUntyped then allAnnots.filterNot(_ === Annot.Untyped) else allAnnots + val isUntyped = allAnnots.exists(_.isInstanceOf[Annot.Untyped]) + val annots = if isUntyped then allAnnots.filterNot(_.isInstanceOf[Annot.Untyped]) else allAnnots val k: Result => Block = if !isUntyped then k0 else r => @@ -768,7 +768,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case st.UnitVal() => k(unit) case st.Lit(lit) => if lit =/= Tree.UnitLit(false) then warnStmt - k(Value.Lit(lit)) + k(Value.Lit(lit)(trm.toLoc)) case st.Ret(res) => returnedTerm(res, loweringCtx.returnType) case st.Throw(res) => @@ -793,7 +793,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): Call( State.builtinOpsMap("===").asSimpleRef, (bodyResult.asSimpleRef.asArg :: State.runtimeSymbol.asSimpleRef.selSN("Continue").asArg :: Nil) ne_:: Nil, - )(CallMetadata.defaultMlsFun), + )(CallMetadata.defaultMlsFun, trm.toLoc), Match( isContinue.asSimpleRef, (Case.Lit(Tree.BoolLit(true)) -> Continue(label)) :: Nil, @@ -811,18 +811,18 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case st.Break(label, result, value) => value match case S(v) => term(v)(r => Assign(result, r, Break(label))) - case N => Assign(result, Value.Lit(Tree.UnitLit(false)), Break(label)) + case N => Assign(result, Value.Lit(Tree.UnitLit(false))(trm.toLoc), Break(label)) case st.Continue(label) => Continue(label) case st.Asc(lhs, rhs) => term(lhs, inStmtPos = inStmtPos)(k) case st.Tup(fs) => - args(fs, Nil)(args => k(Tuple(mut = false, args))) + args(fs, Nil)(args => k(Tuple(mut = false, args)(trm.toLoc))) case Mut(st.Tup(fs)) => - args(fs, Nil)(args => k(Tuple(mut = true, args))) + args(fs, Nil)(args => k(Tuple(mut = true, args)(trm.toLoc))) case st.CtxTup(fs) => // * This case is currently triggered for code such as `f(using 42)` - args(fs, Nil)(args => k(Tuple(mut = false, args))) + args(fs, Nil)(args => k(Tuple(mut = false, args)(trm.toLoc))) case t @ st.Ref(sym) => ref(t, annots, N, inStmtPos = inStmtPos)(k) case st.Resolved(t @ st.Ref(bsym), sym) => @@ -843,7 +843,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): subTerm(arg): ar => val target = wasmIntrinsicPath(sym, unary = true) .getOrElse(sym.asSimpleRef.withLocOf(ref)) - k(Call(target, (Arg(N, ar) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun)) + k(Call(target, (Arg(N, ar) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, trm.toLoc)) case st.Tup(Fld(FldFlags.benign(), arg1, N) :: Fld(FldFlags.benign(), arg2, N) :: Nil) => if !sym.binary then raise: ErrorReport( @@ -856,7 +856,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): if k.isInstanceOf[TailOp] then Match( ar1, (Case.Lit(posLit) -> term_nonTail(arg2)(k)) :: Nil, - S(k(Value.Lit(negLit))), + S(k(Value.Lit(negLit)(trm.toLoc))), Unreachable("tail operation in branches"), ) else // Well-typed short-circuit expressions have Boolean operands and results. @@ -866,7 +866,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): Match( ar1, (Case.Lit(posLit) -> term_nonTail(arg2)(Assign(ts, _, End()))) :: Nil, - S(Assign(ts, Value.Lit(negLit), End())), + S(Assign(ts, Value.Lit(negLit)(trm.toLoc), End())), k(ts.asSimpleRef), ) sym match @@ -876,7 +876,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): subTerm_nonTail(arg2): ar2 => val target = wasmIntrinsicPath(sym, unary = false) .getOrElse(sym.asSimpleRef.withLocOf(ref)) - k(Call(target, (Arg(N, ar1) :: Arg(N, ar2) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun)) + k(Call(target, (Arg(N, ar1) :: Arg(N, ar2) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, trm.toLoc)) case _ => fail: ErrorReport( msg"Unexpected arguments for builtin symbol '${sym.nme}'" -> arg.toLoc :: Nil, S(arg), @@ -973,16 +973,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // * are preserved in the call and not moved to a temporary variable. case sel @ Sel(prefix, nme) => subTerm(prefix): p => - conclude(Select(p, nme)(N)(false).withLocOf(sel)) + conclude(Select(p, nme)(N, sel.toLoc)(false)) case Resolved(sel @ Sel(prefix, nme), sym) => subTerm(prefix): p => - conclude(Select(p, definitionIdent(nme, sym))(S(sym))(false).withLocOf(sel)) + conclude(Select(p, definitionIdent(nme, sym))(S(sym), trm.toLoc)(false).withLocOf(sel)) case sel @ SelProj(prefix, _, nme) => subTerm(prefix): p => - conclude(Select(p, nme)(N)(false).withLocOf(sel)) + conclude(Select(p, nme)(N, sel.toLoc)(false)) case Resolved(sel @ SelProj(prefix, _, nme), sym) => subTerm(prefix): p => - conclude(Select(p, definitionIdent(nme, sym))(S(sym))(false).withLocOf(sel)) + conclude(Select(p, definitionIdent(nme, sym))(S(sym), trm.toLoc)(false).withLocOf(sel)) case _ => subTerm(baseF)(conclude) case h @ Handle(lhs, rhs, as, cls, defs, bod) => if config.effectHandlers.isEmpty then @@ -1067,7 +1067,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val affineAnnots = annots.collect: case a @ Annot.Affine(0) => a if k.isInstanceOf[TailOp] || bodyBlock.size <= 5 - then k(Lambda(paramLists.head, bodyBlock)(affineAnnots)) + then k(Lambda(paramLists.head, bodyBlock)(affineAnnots, trm.toLoc)) else val lamSym = new BlockMemberSymbol("lambda", Nil, false) loweringCtx.collectScopedSym(lamSym) @@ -1084,9 +1084,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case whltrm: st.SynthWhile => ucs.Normalization(this)(whltrm)(k) case sel @ Sel(prefix, nme) => - setupSelection(prefix, nme, N)(k) + setupSelection(prefix, nme, N, sel.toLoc)(k) case Resolved(sel @ Sel(prefix, nme), sym) => - setupSelection(prefix, nme, S(sym))(k) + setupSelection(prefix, nme, S(sym), sel.toLoc)(k) case sel @ SynthSel(prefix, nme) => // * Not using `setupSelection` as these selections are not meant to be sanity-checked @@ -1099,16 +1099,16 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): subTerm(prefix): p => k(Select(p, memberIdent(nme, sel.sym))(sel.sym.collect: case s: DefinitionSymbol[?] => s - )(false)) + , trm.toLoc)(false)) case Resolved(sel @ SynthSel(prefix, nme), sym) => // * Not using `setupSelection` as these selections are not meant to be sanity-checked subTerm(prefix): p => - k(Select(p, definitionIdent(nme, sym))(S(sym))(false)) + k(Select(p, definitionIdent(nme, sym))(S(sym), trm.toLoc)(false)) case DynSel(prefix, fld, ai) => subTerm(prefix): p => subTerm_nonTail(fld): f => - k(DynSelect(p, f, ai)) + k(DynSelect(p, f, ai)(trm.toLoc)) case nw @ (_: New | _: DynNew | Mut(_: New | _: DynNew)) => @@ -1120,7 +1120,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case _ => spuriousWarning subTerm(cls): sr => rft match - case N => lowerMultiInstantiate(mut, sr, as, annots)(k) + case N => lowerMultiInstantiate(mut, sr, as, annots, trm.toLoc)(k) case S((isym, rft)) => val sym = new BlockMemberSymbol(isym.name, Nil) loweringCtx.collectScopedSym(sym) @@ -1143,23 +1143,23 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // * InvalML-specific cases: t.Cls#field and mutable operations case sp @ SelProj(prefix, _, proj) => - setupSelection(prefix, proj, N)(k) + setupSelection(prefix, proj, N, sp.toLoc)(k) case Resolved(sp @ SelProj(prefix, _, proj), sym) => - setupSelection(prefix, proj, S(sym))(k) + setupSelection(prefix, proj, S(sym), sp.toLoc)(k) case Resolved(inner, sym) => TODO(s"lowering for Resolved($inner)") case Region(reg, body) => loweringCtx.collectScopedSym(reg) - Assign(reg, Instantiate(mut = true, Select(State.globalThisSymbol.asThis, Tree.Ident("Region"))(N)(false), Nil :: Nil)(InstantiateMetadata.empty), + Assign(reg, Instantiate(mut = true, Select(State.globalThisSymbol.asThis, Tree.Ident("Region"))(N, trm.toLoc)(false), Nil :: Nil)(InstantiateMetadata.empty, trm.toLoc), term_nonTail(body)(k)) case RegRef(reg, value) => plainArgs(reg :: value :: Nil): args => - k(Instantiate(mut = true, Select(State.globalThisSymbol.asThis, Tree.Ident("Ref"))(N)(false), args :: Nil)(InstantiateMetadata.empty)) + k(Instantiate(mut = true, Select(State.globalThisSymbol.asThis, Tree.Ident("Ref"))(N, trm.toLoc)(false), args :: Nil)(InstantiateMetadata.empty, trm.toLoc)) case Drop(ref) => subTerm(ref): _ => k(unit) case Deref(ref) => subTerm(ref): r => - k(Select(r, Tree.Ident("value"))(N)(false)) + k(Select(r, Tree.Ident("value"))(N, trm.toLoc)(false)) case SetRef(lhs, rhs) => subTerm(lhs): ref => subTerm_nonTail(rhs): value => @@ -1167,9 +1167,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case Mut(Rcd(mut, stats)) => // TODO: warn when in statement position // * Note: I don't think this is supposed to happen... - block(stats, L(mut -> Nil))(k) + block(stats, L((mut, Nil, trm.toLoc)))(k) case Rcd(mut, stats) => // TODO: warn when in statement position - block(stats, L(mut -> Nil))(k) + block(stats, L((mut, Nil, trm.toLoc)))(k) case Missing => fail: ErrorReport( @@ -1188,17 +1188,17 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): // subTerm(t)(k) def setupTerm(name: Str, args: Ls[Path])(k: Result => Block)(using LoweringCtx): Block = - k(Instantiate(mut = false, State.termSymbol.asSimpleRef.selSN(name), args.map(_.asArg) :: Nil)(InstantiateMetadata.empty)) + k(Instantiate(mut = false, State.termSymbol.asSimpleRef.selSN(name), args.map(_.asArg) :: Nil)(InstantiateMetadata.empty, N)) def setupQuotedKeyword(kw: Str): Path = State.termSymbol.asSimpleRef.selSN("Keyword").selSN(kw) def setupSymbol(symbol: ValueSymbol)(k: Result => Block)(using LoweringCtx): Block = k(Instantiate(mut = false, State.termSymbol.asSimpleRef.selSN("Symbol"), - (Value.Lit(Tree.StrLit(symbol.nme)).asArg :: Nil) :: Nil)(InstantiateMetadata.empty)) + (Value.Lit(Tree.StrLit(symbol.nme))(N).asArg :: Nil) :: Nil)(InstantiateMetadata.empty, N)) def quotePattern(p: FlatPattern)(k: Result => Block)(using LoweringCtx): Block = p match - case FlatPattern.Lit(lit) => setupTerm("LitPattern", Value.Lit(lit) :: Nil)(k) + case FlatPattern.Lit(lit) => setupTerm("LitPattern", Value.Lit(lit)(N) :: Nil)(k) case _ => // TODO fail: ErrorReport( @@ -1246,10 +1246,10 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): def quote(t: st)(k: Result => Block)(using LoweringCtx): Block = t match case Lit(lit) => - setupTerm("Lit", Value.Lit(lit) :: Nil)(k) + setupTerm("Lit", Value.Lit(lit)(N) :: Nil)(k) case Ref(sym) if Elaborator.binaryOps.contains(sym.nme) => // builtin symbols val l = loweringCtx.registerTempSymbol(N, erasedType = N) - setupTerm("Builtin", Value.Lit(Tree.StrLit(sym.nme)) :: Nil)(k) + setupTerm("Builtin", Value.Lit(Tree.StrLit(sym.nme))(N) :: Nil)(k) case Resolved(Ref(sym), disamb) => sym match case sym: BlockMemberSymbol => k(sym.asMemberRef(disamb)) @@ -1260,8 +1260,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case SynthSel(Ref(sym: ModuleOrObjectSymbol), name) => // Module/object cross-stage references setupSymbol(sym): r1 => val l1, l2 = loweringCtx.registerTempSymbol(N, erasedType = N) - Assign(l1, r1, setupTerm("CSRef", l1.asSimpleRef :: setupFilename :: Value.Lit(syntax.Tree.UnitLit(false)) :: Nil)(r2 => - Assign(l2, r2, setupTerm("Sel", l2.asSimpleRef :: Value.Lit(syntax.Tree.StrLit(name.name)) :: Nil)(k)) + Assign(l1, r1, setupTerm("CSRef", l1.asSimpleRef :: setupFilename :: Value.Lit(syntax.Tree.UnitLit(false))(N) :: Nil)(r2 => + Assign(l2, r2, setupTerm("Sel", l2.asSimpleRef :: Value.Lit(syntax.Tree.StrLit(name.name))(N) :: Nil)(k)) )) case SynthSel(Ref(sym: BlockMemberSymbol), name) => // Multi-file cross-stage references if config.qqEnabled then fail: @@ -1276,8 +1276,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val basePath = base.up val targetPath = filename val relPath = targetPath.relativeTo(basePath).map(_.toString).getOrElse(targetPath.toString) - Assign(l1, r1, setupTerm("CSRef", l1.asSimpleRef :: setupFilename :: Value.Lit(syntax.Tree.StrLit(relPath)) :: Nil)(r2 => - Assign(l2, r2, setupTerm("Sel", l2.asSimpleRef :: Value.Lit(syntax.Tree.StrLit(name.name)) :: Nil)(k)) + Assign(l1, r1, setupTerm("CSRef", l1.asSimpleRef :: setupFilename :: Value.Lit(syntax.Tree.StrLit(relPath))(N) :: Nil)(r2 => + Assign(l2, r2, setupTerm("Sel", l2.asSimpleRef :: Value.Lit(syntax.Tree.StrLit(name.name))(N) :: Nil)(k)) )) case _ => fail: ErrorReport( @@ -1292,7 +1292,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val arr = loweringCtx.registerTempSymbol(N, erasedType = S(ErasedType.Array), "arr") Assign( arr, - Tuple(mut = false, ds.reverse.map(_.asArg)), + Tuple(mut = false, ds.reverse.map(_.asArg))(N), Assign(l, r, setupTerm("Lam", arr.asSimpleRef :: l.asSimpleRef :: Nil)(k))) case sym :: rest => loweringCtx.collectScopedSym(sym) @@ -1307,7 +1307,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): val arrSym = loweringCtx.registerTempSymbol(N, erasedType = S(ErasedType.Array), "arr") Assign( arrSym, - Tuple(mut = false, xs.reverse.map(_.asArg)), + Tuple(mut = false, xs.reverse.map(_.asArg))(N), setupTerm("Tup", arrSym.asSimpleRef :: Nil): r2 => val l1 = loweringCtx.registerTempSymbol(N, erasedType = N) val l2 = loweringCtx.registerTempSymbol(N, erasedType = N) @@ -1336,7 +1336,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): .chain(b => quote(res)(r3 => Assign(l3, r3, b))) .chain(b => setupTerm("LetDecl", l1.asSimpleRef :: Nil)(r4 => Assign(l4, r4, b))) .chain(b => setupTerm("DefineVar", l1.asSimpleRef :: l2.asSimpleRef :: Nil)(r5 => Assign(l5, r5, b))) - .assign(arrSym, Tuple(mut = false, (l4 :: l5 :: Nil).map(s => s.asSimpleRef.asArg))) + .assign(arrSym, Tuple(mut = false, (l4 :: l5 :: Nil).map(s => s.asSimpleRef.asArg))(N)) .rest(setupTerm("Blk", arrSym.asSimpleRef :: l3.asSimpleRef :: Nil)(k)) } case IfLike(_, IfLikeForm.ReturningIf, split) => quoteSplit(split.getExpandedSplit, Map.empty): r => @@ -1425,7 +1425,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): b, Assign( rcdSym, - Record(mut = false, fsr.reverse), + Record(mut = false, fsr.reverse)(N), k((Arg(N, rcdSym.asSimpleRef) :: asr).reverse))) @@ -1492,9 +1492,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): ) - def setupSelection(prefix: Term, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]])(k: Result => Block)(using LoweringCtx): Block = + def setupSelection(prefix: Term, nme: Tree.Ident, disamb: Opt[DefinitionSymbol[?]], loc: Opt[Loc])(k: Result => Block)(using LoweringCtx): Block = subTerm(prefix): p => - k(Select(p, disamb.fold(memberIdent(nme, N))(definitionIdent(nme, _)))(disamb)( + k(Select(p, disamb.fold(memberIdent(nme, N))(definitionIdent(nme, _)))(disamb, loc)( !disamb.isDefined // * ^ We assume that resolved selections are well-behaved (will not yield undefined or debind a method) // || disamb.exists(_.defn.exists(_.hasDeclareModifier.isEmpty)) // * This checks `declare` members, which is normally unwanted @@ -1503,7 +1503,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): final def setupFunctionOrByNameDef(paramLists: List[ParamList], bodyTerm: Term, name: Option[Str], returnType: Opt[ErasedType]) (using LoweringCtx): (List[ParamList], Block) = val physicalParams = paramLists match - case Nil => ParamList(ParamListFlags.empty, Nil, N) :: Nil + case Nil => ParamList(ParamListFlags.empty, Nil, N)(N) :: Nil case ps => ps setupFunctionDef(physicalParams, bodyTerm, name, returnType) @@ -1529,22 +1529,22 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): case S(value) => WarningReport(value -> annot.toLoc :: Nil) case N => WarningReport(msg"This annotation has no effect." -> annot.toLoc :: Nil) annotations.foreach: - case Annot.Untyped => () - case a @ (Annot.TailRec | Annot.Inline | Annot.NoInline | Annot.Generator | Annot.Async | Annot.RaiseEffects) => + case Annot.Untyped() => () + case a @ (Annot.TailRec() | Annot.Inline() | Annot.NoInline() | Annot.Generator() | Annot.Async() | Annot.RaiseEffects()) => val annot = a match - case Annot.TailRec => "@tailrec" - case Annot.Inline => "@inline" - case Annot.NoInline => "@noInline" - case Annot.Generator => "@generator" - case Annot.Async => "@async" - case Annot.RaiseEffects => "@raiseEffects" + case Annot.TailRec() => "@tailrec" + case Annot.Inline() => "@inline" + case Annot.NoInline() => "@noInline" + case Annot.Generator() => "@generator" + case Annot.Async() => "@async" + case Annot.RaiseEffects() => "@raiseEffects" target match case TermDefinition(body = S(bod), k = syntax.Fun) => () case TermDefinition(k = syntax.Fun) => warn(a, S(msg"Only functions with a body may be marked as $annot.")) case _ => warn(a) case Annot.Modifier(syntax.Keyword.`public` | syntax.Keyword.`private` | syntax.Keyword.`virtual`) => () case Annot.Modifier(syntax.Keyword("staged")) => () - case Annot.Pure => () + case Annot.Pure() => () case a: Annot.Affine => target match case TermDefinition(k = syntax.Fun) => () case _ => warn(a) @@ -1562,13 +1562,13 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): message -> receiver.toLoc :: Nil) annotations.foreach: - case Annot.Untyped => () + case Annot.Untyped() => () case annot: Annot.Trm => receiver match case st.App(Ref(_: BuiltinSymbol), _) => warn(annot) case st.App(_, _) | New(_, _, _) | DynNew(_, _) | Mut(_: New | _: DynNew) => () case st.Resolved(_, defnSym) if isImplicitNullaryCall(defnSym) => () case _ => warn(annot) - case a @ Annot.TailCall => receiver match + case a @ Annot.TailCall() => receiver match case st.App(Ref(_: BuiltinSymbol), _) => warn(a, S(msg"The @tailcall annotation has no effect on calls to built-in symbols.")) case st.App(_, _) => () case st.Resolved(_, defnSym) if isImplicitNullaryCall(defnSym) => () @@ -1585,14 +1585,14 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) private def selFromGlobalThis(path: Str*): Path = path.foldLeft[Path](State.globalThisSymbol.asThis): - (qual, name) => Select(qual, Tree.Ident(name))(N)(false) + (qual, name) => Select(qual, Tree.Ident(name))(N, N)(false) private def assignStmts(stmts: (Assignable, Result)*)(rest: Block) = stmts.foldRight(rest): case ((sym, res), acc) => Assign(sym, res, acc) private def pureCall(fn: Path, args: Ls[Arg]): Result = - Call(fn, args ne_:: Nil)(CallMetadata.defaultMlsFun) + Call(fn, args ne_:: Nil)(CallMetadata.defaultMlsFun, N) extension (k: Block => Block) def |>: (b: Block): Block = k(b) @@ -1640,10 +1640,10 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) val resInspectedSym = loweringCtx.registerTempSymbol(N, erasedType = N, dbgNme = "traceLogResInspected") - val psSymArgs = psInspectedSyms.zipWithIndex.foldRight[Ls[Arg]](Arg(N, Value.Lit(Tree.StrLit(")"))) :: Nil): + val psSymArgs = psInspectedSyms.zipWithIndex.foldRight[Ls[Arg]](Arg(N, Value.Lit(Tree.StrLit(")"))(N)) :: Nil): case (((s, p), i), acc) => if i == psInspectedSyms.length - 1 then Arg(N, s.asSimpleRef) :: acc - else Arg(N, s.asSimpleRef) :: Arg(N, Value.Lit(Tree.StrLit(", "))) :: acc + else Arg(N, s.asSimpleRef) :: Arg(N, Value.Lit(Tree.StrLit(", "))(N)) :: acc val tmp1, tmp2, tmp3 = loweringCtx.registerTempSymbol(N, erasedType = N) @@ -1653,7 +1653,7 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) assignStmts( enterMsgSym -> pureCall( strConcatFn, - Arg(N, Value.Lit(Tree.StrLit(s"CALL ${name.getOrElse("[arrow function]")}("))) :: psSymArgs + Arg(N, Value.Lit(Tree.StrLit(s"CALL ${name.getOrElse("[arrow function]")}("))(N)) :: psSymArgs ), tmp1 -> pureCall(traceLogFn, Arg(N, enterMsgSym.asSimpleRef) :: Nil), prevIndentLvlSym -> pureCall(traceLogIndentFn, Nil) @@ -1664,7 +1664,7 @@ trait LoweringTraceLog(instrument: Bool)(using TL, Raise, State) resInspectedSym -> pureCall(inspectFn, Arg(N, resSym.asSimpleRef) :: Nil), retMsgSym -> pureCall( strConcatFn, - Arg(N, Value.Lit(Tree.StrLit("=> "))) :: Arg(N, resInspectedSym.asSimpleRef) :: Nil + Arg(N, Value.Lit(Tree.StrLit("=> "))(N)) :: Arg(N, resInspectedSym.asSimpleRef) :: Nil ), tmp2 -> pureCall(traceLogResetFn, Arg(N, prevIndentLvlSym.asSimpleRef) :: Nil), tmp3 -> pureCall(traceLogFn, Arg(N, retMsgSym.asSimpleRef) :: Nil) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala index 32fbebb2f9..e266a55503 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/ReflectionInstrumenter.scala @@ -38,7 +38,7 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n case b: Bool => Tree.BoolLit(b) case s: Str => Tree.StrLit(s) case n: BigDecimal => Tree.DecLit(n) - Value.Lit(l) + Value.Lit(l)(l.toLoc) extension [A, B](ls: Ls[(A => B) => B]) def collectApply(f: Ls[A] => B): B = @@ -58,11 +58,11 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n Scoped(Set(sym), Assign(sym, res, k(sym.asSimpleRef))) def tuple(elems: Ls[ArgWrappable], symName: Str = "tmp")(k: Path => Block): Block = - assign(Tuple(false, elems.map(asArg)), symName)(k) + assign(Tuple(false, elems.map(asArg))(N), symName)(k) // isMlsFun is probably always true? def call(fun: Path, args: Ls[ArgWrappable], isMlsFun: Bool = true, symName: Str = "tmp")(k: Path => Block): Block = - assign(Call(fun, args.map(asArg) ne_:: Nil)(CallMetadata(isMlsFun, false, Nil)), symName)(k) + assign(Call(fun, args.map(asArg) ne_:: Nil)(CallMetadata(isMlsFun, false, Nil), N), symName)(k) // helpers for instrumenting Block @@ -217,14 +217,14 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n raise(ErrorReport(msg"Instantiate with multiple argument lists not supported in staged module." -> r.toLoc :: Nil)) End() // desugar Runtime.Tuple.get into Select - case Call(fun, Ls(Arg(_, scrut), Arg(_, Value.Lit(Tree.IntLit(idx)))) :: _) if fun == Value.SimpleRef(State.runtimeSymbol).selSN("Tuple").selSN("get") => - transformPath(Select(scrut, Tree.Ident(idx.toString()))(N)(false))(k) + case Call(fun, Ls(Arg(_, scrut), Arg(_, Value.Lit(Tree.IntLit(idx)))) :: _) if fun == Value.SimpleRef(State.runtimeSymbol)(N).selSN("Tuple").selSN("get") => + transformPath(Select(scrut, Tree.Ident(idx.toString()))(N, r.toLoc)(false))(k) case Call(fun, argss) => val stagedFunPath = fun match case s @ Select(qual, Tree.Ident(name)) => s.symbol.flatMap({ case t: TermSymbol => t.owner.flatMap({ case sym: DefinitionSymbol[?] => sym.defn.flatMap(_.hasStagedModifier.map(_ => - Select(qual, Tree.Ident(name + "_gen"))(N)(false) + Select(qual, Tree.Ident(name + "_gen"))(N, N)(false) )) }) case _ => N @@ -265,7 +265,7 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n transformOption(pOpt, transformParamList)(k) def transformCase(cse: Case)(using Context)(k: Path => Block): Block = cse match - case Case.Lit(lit) => blockCtor("Lit", Ls(Value.Lit(lit)))(k) + case Case.Lit(lit) => blockCtor("Lit", Ls(Value.Lit(lit)(N)))(k) case Case.Cls(cls, path) => transformSymbol(cls): cls => transformPath(path): path => @@ -308,7 +308,7 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n // * owned field symbol is selected on its owner rather than emitted as a // * plain reference (which would otherwise reach `JSBuilder`'s owned-`SimpleRef` path). ((cont: Block) => AssignField(lhs, nme, xStaged, cont)(S(ts))): - given Context = ctx.clone() += Select(lhs, nme)(S(ts))(false) -> xStaged + given Context = ctx.clone() += Select(lhs, nme)(S(ts), N)(false) -> xStaged transformBlock(rest): (z, ctx) => blockCtor("Assign", Ls(xSym, y, z), "assign")(k(_, ctx)) case _ => @@ -384,7 +384,7 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n // TODO: remove it. only for test val debug = (k: Block) => call(sym, Nil)(fnPrintCode(_)(k)) - val newFun = f.copy(sym = genSym, dSym = dSym, params = Ls(PlainParamList(Nil)), body = newBody)(f.configOverride, f.annotations) + val newFun = f.copy(sym = genSym, dSym = dSym, params = Ls(PlainParamList(Nil)(N)), body = newBody)(f.configOverride, f.annotations) (newFun, debug) override def applyBlock(b: Block): Block = super.applyBlock(b) match @@ -395,7 +395,7 @@ class ReflectionInstrumenter(using State, Raise, Ctx) extends BlockTransformer(n val (stagedMethods, debugPrintCode) = companion.methods .map(applyFunDefnInner) .unzip - val ctor = FunDefn.withFreshSymbol(S(companion.isym), BlockMemberSymbol("ctor$", Nil), Ls(PlainParamList(Nil)), companion.ctor)(N, Nil) + val ctor = FunDefn.withFreshSymbol(S(companion.isym), BlockMemberSymbol("ctor$", Nil), Ls(PlainParamList(Nil)(N)), companion.ctor)(N, Nil) val (stagedCtor, ctorPrint) = applyFunDefnInner(ctor) val debugBlock = (ctorPrint :: debugPrintCode) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala index 031a1b915e..9d08855a60 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/TailRecOpt.scala @@ -255,7 +255,7 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val rest = restArgs match case Arg(S(SpreadKind.Eager), value) :: Nil => value - case _ => Tuple(true, restArgs) + case _ => Tuple(true, restArgs)(N) CallArgsResult.Success(hd.appended(rest)) else CallArgsResult.Success(hd) @@ -279,11 +279,11 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val tupleSym = TempSymbol(N, erasedType = S(ErasedType.Array), "argList") - val tupleRes = Tuple(false, args) + val tupleRes = Tuple(false, args)(N) // Main args def mainArgs(rest: List[Path]) = (0 until paramList.size).toList.foldRight(rest): - case (n, acc) => DynSelect(tupleSym.asSimpleRef, Value.Lit(Tree.IntLit(n)), true) :: acc + case (n, acc) => DynSelect(tupleSym.asSimpleRef, Value.Lit(Tree.IntLit(n))(N), true)(N) :: acc // If the rest param exists, append a slice val (initialBlk: (Block => Block), pathList: List[Path]) = @@ -295,10 +295,10 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): .sel(Tree.Ident("Tuple"), State.tupleSymbol) .sel(Tree.Ident("slice"), State.tupleSliceSymbol), (tupleSym.asSimpleRef.asArg - :: Value.Lit(Tree.IntLit(paramList.length)).asArg - :: Value.Lit(Tree.IntLit(0)).asArg + :: Value.Lit(Tree.IntLit(paramList.length))(N).asArg + :: Value.Lit(Tree.IntLit(0))(N).asArg :: Nil) ne_:: Nil - )(CallMetadata.defaultMlsFun) + )(CallMetadata.defaultMlsFun, N) val blk = blockBuilder .assignScoped(tupleSym, tupleRes) .assignScoped(sliceResSym, sliceRes) @@ -474,7 +474,7 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val curIdSym = VarSymbol(Tree.Ident("id"), erasedType = S(ErasedType.Int)) val loopDefnPath = owner match - case Some(value) => Select(value.asThis, Tree.Ident(bms.nme))(S(dSym))(false) + case Some(value) => Select(value.asThis, Tree.Ident(bms.nme))(S(dSym), N)(false) case None => bms.asMemberRef(dSym) def rewriteKnownCall(callee: FunDefn, flattenedArgs: List[Path]) = @@ -486,9 +486,9 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val args = paramSyms.map: s => argsMap.get(s) match case Some(pth) => Arg(N, pth) - case None => Arg(N, Value.Lit(Tree.UnitLit(false))) - val argsWithId = if funsLen > 1 then Value.Lit(Tree.IntLit(dSymIds(callee.dSym))).asArg :: args else args - Call(loopDefnPath, argsWithId ne_:: Nil)(CallMetadata.defaultMlsFun) + case None => Arg(N, Value.Lit(Tree.UnitLit(false))(N)) + val argsWithId = if funsLen > 1 then Value.Lit(Tree.IntLit(dSymIds(callee.dSym)))(N).asArg :: args else args + Call(loopDefnPath, argsWithId ne_:: Nil)(CallMetadata.defaultMlsFun, N) class FunRewriter(f: FunDefn) extends BlockTransformerShallow(SymbolSubst.Id): val params = f.allParamSyms @@ -557,7 +557,7 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): Match( o.flag.asSimpleRef, Case.Lit(Tree.BoolLit(true)) -> - Assign(slot, Cast(ref, o.tpe, config.checkCasts), End()) :: Nil, + Assign(slot, Cast(ref, o.tpe, config.checkCasts)(N), End()) :: Nil, N, acc))) @@ -585,12 +585,12 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val cont = val resume = if funsLen === 1 then Continue(loopSym) - else Assign(curIdSym, Value.Lit(Tree.IntLit(dSymIds(calleeSym))), Continue(loopSym)) + else Assign(curIdSym, Value.Lit(Tree.IntLit(dSymIds(calleeSym)))(N), Continue(loopSym)) // Directly emitting the jump causes the return coercion to be dropped, so we record the cast and defer // its emission to the loop's exit. deferredCasts.get(f.dSym) match case S(o) if retCoercion.exists(t => !alwaysSucceedsAtExits(t)) => - Assign(o.flag, Value.Lit(Tree.BoolLit(true)), resume) + Assign(o.flag, Value.Lit(Tree.BoolLit(true))(N), resume) case _ => resume // In some cases, we could have assignments like this: // param0 = whatever @@ -741,7 +741,7 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): else Scoped( deferredCasts.iterator.map(_._2.flag).toSet, deferredCasts.foldRight[Block](labelled): - case ((_, o), acc) => Assign(o.flag, Value.Lit(Tree.BoolLit(false)), acc)) + case ((_, o), acc) => Assign(o.flag, Value.Lit(Tree.BoolLit(false))(N), acc)) if !hasWrapper then val f = funs.head @@ -759,16 +759,16 @@ class TailRecOpt(checkAnnotations: Bool)(using Config, State, TL, Raise, Ctx): val newBod = Return( coerceToDeclaredReturn(rewriteKnownCall(f, paramArgs), f.dSym), ) - val annots = if f.inline then f.annotations else Annot.Inline :: f.annotations + val annots = if f.inline then f.annotations else Annot.Inline()(N) :: f.annotations FunDefn(f.owner, f.sym, f.dSym, f.params, newBod)(N, annots) val newParamLists = val initial = paramSyms.map(Param.simple(_)) if funsLen > 1 then - PlainParamList(Param.simple(curIdSym) :: initial) :: Nil + PlainParamList(Param.simple(curIdSym) :: initial)(N) :: Nil else - PlainParamList(initial) :: Nil + PlainParamList(initial)(N) :: Nil val annotations = - if funsLen == 1 && funs.head.inline then Annot.Inline :: Annot.Private :: Nil + if funsLen == 1 && funs.head.inline then Annot.Inline()(N) :: Annot.Private :: Nil else Annot.Private :: Nil val loopDefn = FunDefn( owner, bms, dSym, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala index 0ee5b1028b..5eea405f22 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/WorkerWrapper.scala @@ -25,10 +25,10 @@ class WorkerWrapper import tl.* private def withInline(annotations: Ls[Annot]): Ls[Annot] = - if annotations.contains(Annot.Inline) then annotations else Annot.Inline :: annotations + if annotations.exists(_.isInstanceOf[Annot.Inline]) then annotations else Annot.Inline()(N) :: annotations private def withoutInline(annotations: Ls[Annot]): Ls[Annot] = - annotations.filterNot(_ == Annot.Inline) + annotations.filterNot(_.isInstanceOf[Annot.Inline]) private def isPlainParamList(params: ParamList): Bool = params.flags == ParamListFlags.empty && params.restParam.isEmpty @@ -53,7 +53,7 @@ class WorkerWrapper val mapping = collection.mutable.LinkedHashMap.empty[Symbol, Symbol] val flatParams = params.flatMap: params => params.params.map(freshParam(_, mapping)) - PlainParamList(flatParams) -> mapping.toMap + PlainParamList(flatParams)(Loc(params)) -> mapping.toMap private def rewriteParams(body: Block, mapping: Map[Symbol, Symbol]): Block = val subst = new SymbolSubst: @@ -76,7 +76,7 @@ class WorkerWrapper val workerArgs = fun.params.flatMap(_.params).map: param => Arg(N, param.sym.asSimpleRef) val wrapperBody = Return( - Call(worker.asPath, workerArgs ne_:: Nil)(CallMetadata.mlsFunWithEffect), + Call(worker.asPath, workerArgs ne_:: Nil)(CallMetadata.mlsFunWithEffect, N), ) val wrapper = FunDefn( fun.owner, @@ -85,7 +85,7 @@ class WorkerWrapper fun.params, wrapperBody, )(fun.configOverride, withInline(fun.annotations.filter: - case Annot.NoInline | Annot.Generator => false + case Annot.NoInline() | Annot.Generator() => false case _ => true )) log(s"▶ Worker-wrapper: ${fun.dSym.showDbg} -> ${worker.dSym.showDbg}") 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 12bcfab9af..32d149f608 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -32,7 +32,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) case l: LabelSymbol => l -> instId case scrutId: ResultId => ConcreteId(scrutId, instId) extension (vs: Ls[VarSymbol]) def asParamList: ParamList = - ParamList(ParamListFlags.empty, vs.map(Param.simple), N) + ParamList(ParamListFlags.empty, vs.map(Param.simple), N)(N) extension (c: CtorCls) def ctorClsName: String = c match case cls: ClassLikeSymbol => cls.nme case n: Int => s"tup$n" @@ -72,8 +72,8 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) .asInstanceOf[Iterator[Label | Match]] parents -> blockUntilParent - private val PrivateModifier = Annot.Modifier(syntax.Keyword.`private`) - private val AffineAnnotForBranchFns = Annot.Affine(1) + private val PrivateModifier = Annot.Modifier(syntax.Keyword.`private`)(N) + private val AffineAnnotForBranchFns = Annot.Affine(1)(N) val branchSelSyms = MutMap.empty[CtorDtorId, VarSymbol] // branch fun params for fields (which share the same symbol in `branchSelSyms`) @@ -316,9 +316,9 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) private def mkCall(target: (BlockMemberSymbol, TermSymbol), args: Ls[ValueSymbol]): Result = Call( - Value.MemberRef(target._1, target._2), + Value.MemberRef(target._1, target._2)(N), args.map(a => Arg(N, a.asPath)) ne_:: Nil - )(CallMetadata.defaultMlsFun) + )(CallMetadata.defaultMlsFun, N) // Rewrites the program under a specific instantiation id // from the polymorphic analysis @@ -397,7 +397,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) Call( newScrut, callWithFvs.map(s => Arg(N, s.asPath)) ne_:: Nil - )(CallMetadata.defaultMlsFun)) + )(CallMetadata.defaultMlsFun, N)) case Break(label) => val labelRestFunId = label.withInstId(instId) restFunSyms.get(labelRestFunId) match @@ -438,7 +438,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) case Value.MemberRef(bms, disamb) if existingMapping.contains(bms) => val sym = existingMapping(bms) sym match - case s: VarSymbol => k(Value.SimpleRef(s)) + case s: VarSymbol => k(Value.SimpleRef(s)(v.toLoc)) case _ => super.applyValue(v)(k) case _ => super.applyValue(v)(k) end RefreshSymbol @@ -488,7 +488,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) val newSym = new VarSymbol(Tree.Ident(p.sym.name), erasedType = p.sym.erasedType) refreshParamMap(p.sym) = newSym Param(p.flags, newSym, p.sign, p.modulefulness)(p.toLoc), - pl.restParam) + pl.restParam)(pl.toLoc) val bodyWithCorrectSymbols = refreshExtractedBody(refreshParamMap.toMap, rewrittenBody) FunDefn( N, bms, tSym, refreshedParams, @@ -531,7 +531,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise) transformedOgBody, Return(mkCall(parentFunSym, parentFunFvs))) case None => - Begin(transformedOgBody, Return(Value.Lit(Tree.UnitLit(true)))) + Begin(transformedOgBody, Return(Value.Lit(Tree.UnitLit(true))(N))) val refreshedFvSymbols = restFnFvs(restFunId).map(s => s -> new VarSymbol(Tree.Ident(s"fv_${s.nme}"), erasedType = s.mapErasedValueType)) val bodyWithCorrectSymbols = refreshExtractedBody(refreshedFvSymbols.toMap, actualBody) FunDefn(N, bms, tsym, refreshedFvSymbols.unzip._2.asParamList :: Nil, bodyWithCorrectSymbols)(N, annotations = PrivateModifier :: Nil) 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 19344a2fd4..b15666febd 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala @@ -58,7 +58,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: case Operand(prec: Int) def mkErr(errMsg: Message)(using Raise, Scope): Document = - doc"throw globalThis.Error(${result(Value.Lit(syntax.Tree.StrLit(errMsg.show)))})" + doc"throw globalThis.Error(${result(Value.Lit(syntax.Tree.StrLit(errMsg.show))(N))})" def errExpr(errMsg: Message)(using Raise, Scope): Document = raise(ErrorReport(errMsg -> N :: Nil, @@ -274,9 +274,9 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: paramLists match case Nil => body case params :: Nil => - Return(Lambda(params, body)(if generator then Annot.Generator :: Nil else Nil)) + Return(Lambda(params, body)(if generator then Annot.Generator()(N) :: Nil else Nil, N)) case params :: rest => - Return(Lambda(params, curriedFunctionBody(rest, body, generator))(Nil)) + Return(Lambda(params, curriedFunctionBody(rest, body, generator))(Nil, N)) /** Looks through the casts that a JS program does not materialize. */ @tailrec @@ -352,7 +352,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: else doc"$runtimeVar.safeCall(${calls})" case lam @ Lambda(ps, bod) => scope.nest givenIn: val (params, bodyDoc) = setupFunction(none, ps, bod, isLambda = true) - if lam.annot.contains(Annot.Generator) + if lam.annot.exists(_.isInstanceOf[Annot.Generator]) then // JavaScript has no generator arrows, so bind `this` to preserve the // lexical-`this` behavior of the Lambda IR. @@ -818,7 +818,7 @@ class JSBuilder(using Config, TL, State, Ctx) extends CodeBuilder: case SpecializedSwitch(scrut, cases, dflt, rest) => val switchBod = cases.foldLeft(doc""): (acc, arm) => val needsBreak = arm.isInstanceOf[SwitchCase.ExplicitBreak] - acc :: doc" # case ${result(Value.Lit(arm.litValue))}: #{ ${ + acc :: doc" # case ${result(Value.Lit(arm.litValue)(arm.litValue.toLoc))}: #{ ${ // * Note: we use `block` here so that Scoped nodes will create proper brace sections, // * necessary since `case` clauses do not create a new scope, // * so something like `switch (x) { case 1: let y = 1; break; case 2: let y = 2 }` is ill-formed! 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 c6e2ca397b..4ce5d1aa69 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 @@ -472,7 +472,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui ctorSym = N, k = syntax.Obj, paramsOpt = N, - auxParams = PlainParamList(Nil) :: Nil, + auxParams = PlainParamList(Nil)(N) :: Nil, parentPath = N, methods = Nil, privateFields = Nil, @@ -2235,7 +2235,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui val result = pss.foldRight(bod): case (ps, block) => - Return(Lambda(ps, block)(Nil)) + Return(Lambda(ps, block)(Nil, N)) // Nested functions are not predeclared in `program` - declare them now. // Note that predeclaring functions twice causes an orphaned type to be duplicated in the module. if ctx.getFunc(sym).isEmpty then predeclareTopLevelFun(sym, ps) @@ -2380,7 +2380,7 @@ class WatBuilder(private val ctx: Ctx)(using TraceLogger, State) extends CodeBui clsLikeDefn.methods.foreach: case FunDefn(_, sym, _, Nil, bod) => - overwriteMethod(sym, PlainParamList(Nil), bod) + overwriteMethod(sym, PlainParamList(Nil)(N), bod) case FunDefn(_, sym, _, ps :: Nil, bod) => overwriteMethod(sym, ps, bod) case methodDefn => diff --git a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala index 4f14b8087c..a72b20b5c3 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala @@ -609,7 +609,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): trace[(GeneralType, Type)](s"${ctx.lvl}. Typing ${t.showDbg}", res => s": (${res._1.showDbg}, ${res._2.showDbg})"): given CCtx = CCtx.init(t, N) t match - case Term.Annotated(Annot.Untyped, _) => (Bot, Bot) + case Term.Annotated(Annot.Untyped(), _) => (Bot, Bot) case sel @ Term.SynthSel(Ref(_: TopLevelSymbol), nme) if sel.symbol.isDefined => typeCheck(Ref(sel.symbol.get)(sel.nme, N)) // FIXME 666 diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 19079046e8..6b1e73abde 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -638,8 +638,8 @@ extends Importer: msg"Member names must start with a letter or underscore, followed by letters, digits, or underscores." -> N :: Nil - def mkLetBinding(kw: Tree.Keywrd[?], sym: LocalVarSymbol | TermSymbol, rhs: Term, annotations: Ls[Annot]): Ls[Statement] = - LetDecl(sym, annotations).mkLocWith(kw, sym) :: DefineVar(sym, rhs) :: Nil + def mkLetBinding(sym: LocalVarSymbol | TermSymbol, rhs: Term, annotations: Ls[Annot])(loc: Opt[Loc]): Ls[Statement] = + LetDecl(sym, annotations)(loc) :: DefineVar(sym, rhs)(loc) :: Nil def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[MemberSymbol] = base match @@ -665,12 +665,12 @@ extends Importer: | Keyword.`virtual` | Keyword.`public` | Keyword.`private` - )) => S(Annot.Modifier(kw)) + )) => S(Annot.Modifier(kw)(tree.toLoc)) case App(Ident("config"), Tup(args)) => val modify = ConfigParser.parseOverrides(args) - S(Annot.Config(modify)) + S(Annot.Config(modify)(tree.toLoc)) case App(Ident("affine"), Tup(IntLit(whichParamList) :: Nil)) => - S(Annot.Affine(whichParamList.toInt).withLocOf(tree)) + S(Annot.Affine(whichParamList.toInt)(tree.toLoc)) case _ => term(tree) match case Term.Error() => N case trm => @@ -678,24 +678,24 @@ extends Importer: case S(sym) => sym match case ctx.builtins.annotations.untyped => - return S(Annot.Untyped) + return S(Annot.Untyped()(tree.toLoc)) case ctx.builtins.annotations.tailcall => - return S(Annot.TailCall) + return S(Annot.TailCall()(tree.toLoc)) case ctx.builtins.annotations.tailrec => - return S(Annot.TailRec) + return S(Annot.TailRec()(tree.toLoc)) case ctx.builtins.annotations.inline => - return S(Annot.Inline) + return S(Annot.Inline()(tree.toLoc)) case ctx.builtins.annotations.noInline => - return S(Annot.NoInline) + return S(Annot.NoInline()(tree.toLoc)) case ctx.builtins.annotations.generator => - return S(Annot.Generator) + return S(Annot.Generator()(tree.toLoc)) case ctx.builtins.annotations.async => - return S(Annot.Async) + return S(Annot.Async()(tree.toLoc)) case ctx.builtins.annotations.pure => - return S(Annot.Pure) + return S(Annot.Pure()(tree.toLoc)) case _ => () case _ => () - S(Annot.Trm(trm)) + S(Annot.Trm(trm)(tree.toLoc)) private final case class EffectHandlerMethodSpec( methodName: Str, @@ -734,7 +734,7 @@ extends Importer: Fun, mtdSym, tsym, - PlainParamList(valueSym.fold(Nil)(sym => Param.simple(sym) :: Nil)) :: Nil, + PlainParamList(valueSym.fold(Nil)(sym => Param.simple(sym) :: Nil))(N) :: Nil, N, N, S(spec.methodBody(valueSym)), @@ -1067,7 +1067,7 @@ extends Importer: Param.simple(sym) .toList if params.isEmpty then st - else Term.Lam(PlainParamList(params), st) + else Term.Lam(PlainParamList(params)(N), st) def subterm(tree: Tree): Ctxl[UnderCtx ?=> Term] = trace[Term](s"Elab subterm ${tree.showDbg}", r => s"~> $r"): @@ -1140,7 +1140,7 @@ extends Importer: val lt = subterm(lhs) val sym = TempSymbol(S(lt), erasedType = N, "old") Blk( - LetDecl(sym, Nil) :: DefineVar(sym, lt) :: Nil, Term.Try(Blk( + LetDecl(sym, Nil)(tree.toLoc) :: DefineVar(sym, lt)(tree.toLoc) :: Nil, Term.Try(Blk( Term.Assgn(lt, subterm(rhs)) :: Nil, subterm(bod), ), Term.Assgn(lt, sym.ref()))) @@ -1421,7 +1421,7 @@ extends Importer: Param.simple(self) :: Nil, S: Param.simple(args) - ) + )(N) val rs = FlowSymbol.app() Term.Lam(ps, Term.App(Term.SelProj(self.ref(), c, nme)(f, FlowSymbol.selProj(nme.name), N, S(summon)), args.ref())( @@ -1492,7 +1492,7 @@ extends Importer: val scrut = VarSymbol(Ident("caseScrut"), erasedType = N) val body = caseSplit(scrut, tree) val params = Param.simple(scrut) :: Nil - Term.Lam(PlainParamList(params), body).mkLocWith(kw) + Term.Lam(PlainParamList(params)(N), body).mkLocWith(kw) case PrefixApp(kw @ Keywrd(Keyword.`return`), body) => ctx.getRetHandler match case ReturnHandler.Required(sym) => @@ -1851,7 +1851,7 @@ extends Importer: case pathArg => S((pathArg, N)) val (newCtx, newAcc) = pathAndAlias match case S((StrLit(path), alias)) => - val stmt = importPath(path, alias).withLocOf(m) + val stmt = importPath(path, alias)(m.toLoc) (ctx + (stmt.sym.nme -> stmt.sym), stmt :: acc) case S((pathArg, _)) => @@ -1882,8 +1882,8 @@ extends Importer: val sym = new VarSymbol(id, erasedType = N) newCtx += id.name -> sym RcdField(Term.Lit(StrLit(id.name)).withLocOf(id), sym.ref(id)) - :: DefineVar(sym, rhs_t) - :: LetDecl(sym, annotations) + :: DefineVar(sym, rhs_t)(Loc(rlhs, rhs)) + :: LetDecl(sym, annotations)(id.toLoc) :: acc case lit: Literal => reportUnusedAnnotations @@ -1908,17 +1908,17 @@ extends Importer: case S(rhs) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) - mkLetBinding(kw, sym, term(rrhs), annotations) reverse_::: acc + mkLetBinding(sym, term(rrhs), annotations)(hd.toLoc) reverse_::: acc case N => if tups.nonEmpty then raise(ErrorReport(msg"Expected a right-hand side for let bindings with parameters" -> hd.toLoc :: Nil)) - LetDecl(sym, annotations).mkLocWith(kw) :: acc + LetDecl(sym, annotations)(hd.toLoc) :: acc (ctx + (id.name -> sym)) givenIn: go(sts, Nil, newAcc) case (tree @ LetLike(Keywrd(`let`), lhs, _, N)) :: sts => raise(ErrorReport(msg"Unsupported let binding shape" -> tree.toLoc :: Nil)) go(sts, Nil, Term.Error().withLocOf(tree) :: acc) - case Def(lhs, rhs) :: sts => + case (definition @ Def(lhs, rhs)) :: sts => reportUnusedAnnotations lhs match case id: Ident => @@ -1926,7 +1926,7 @@ extends Importer: ctx.get(id.name) match case S(elem) => elem.symbol match - case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r) :: acc) + case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r)(definition.toLoc) :: acc) case S(sym) => raise(ErrorReport(msg"Symbol '${id.name}' is not a variable and cannot be reassigned" -> id.toLoc :: Nil)) go(sts, Nil, Term.Error().withLocOf(id) :: acc) @@ -1994,8 +1994,8 @@ extends Importer: case _ if ctx.mode is Mode.Light => S(Term.Missing) case S(rhs) => S: val nonLocalRetHandler = TempSymbol(N, erasedType = N, s"nonLocalRetHandler$$${id.name}") - val hasGeneratorAnnotation = annotations.contains(Annot.Generator) - val hasAsyncAnnotation = annotations.contains(Annot.Async) + val hasGeneratorAnnotation = annotations.exists(_.isInstanceOf[Annot.Generator]) + val hasAsyncAnnotation = annotations.exists(_.isInstanceOf[Annot.Async]) if pss.isEmpty && hasGeneratorAnnotation then raise(ErrorReport(msg"Generators are not supported on functions without a parameter list" -> td.toLoc :: Nil)) newCtx.nest(OuterCtx.Function(nonLocalRetHandler)(pss.nonEmpty && hasGeneratorAnnotation, hasAsyncAnnotation)).givenIn: newCtx ?=> @@ -2170,7 +2170,7 @@ extends Importer: res.restParam.foreach: rp => raise(ErrorReport( msg"Spread parameters are not supported in class parameters." -> rp.toLoc :: Nil)) - res.copy(restParam = N) + res.copy(restParam = N)(res.toLoc) def withFields(extraParams: Ls[ParamList])(using Ctx)(fn: (Ctx) ?=> (Term.Blk, Ctx)): (Term.Blk, Ctx) = softAssert(pss.sizeCompare(td.clsParams) === 0, @@ -2216,8 +2216,8 @@ extends Importer: else val psym = TermSymbol(LetBind, owner, p.sym.id, erasedType = p.sym.erasedType) psym.sourceAliases = p.sym.sourceAliases - val decl = LetDecl(psym, Nil) - val defn = DefineVar(psym, p.sym.ref()) + val decl = LetDecl(psym, Nil)(p.toLoc) // TODO: never use term symbols on LetDecl LHS + val defn = DefineVar(psym, p.sym.ref())(p.toLoc) p.fldSym = S(psym) decl :: defn :: Nil @@ -2229,8 +2229,8 @@ extends Importer: case _: TypeAliasSymbol => die val psym = TermSymbol(LetBind, owner, p.sym.id, erasedType = p.sym.erasedType) psym.sourceAliases = p.sym.sourceAliases - val decl = LetDecl(psym, Nil) - val defn = DefineVar(psym, p.sym.ref()) + val decl = LetDecl(psym, Nil)(p.toLoc) + val defn = DefineVar(psym, p.sym.ref())(p.toLoc) p.fldSym = S(psym) decl :: defn :: Nil @@ -2372,7 +2372,7 @@ extends Importer: res.restParam.foreach: rp => raise(ErrorReport( msg"Spread parameters are not supported in class parameters." -> rp.toLoc :: Nil)) - res.copy(restParam = N) + res.copy(restParam = N)(res.toLoc) newCtx.givenIn: trace(s"Processing class definition $nme"): val comp = sym.asMod @@ -2414,14 +2414,14 @@ extends Importer: // * Reconstruct as `Directive(config, Tup(args))` and re-process. case App(Directive(prefix, _), args) :: sts => go(Directive(prefix, args) :: sts, annotations, acc) - case Directive(Ident("config"), Tup(args)) :: sts => + case (directive @ Directive(Ident("config"), Tup(args))) :: sts => reportUnusedAnnotations val modify = ConfigParser.parseOverrides(args) - go(sts, Nil, SetConfig(modify) :: acc) - case Directive(Ident("lang"), Tup(args)) :: sts => + go(sts, Nil, SetConfig(modify)(directive.toLoc) :: acc) + case (directive @ Directive(Ident("lang"), Tup(args))) :: sts => reportUnusedAnnotations val modify = ConfigParser.parseLanguageDirective(args) - go(sts, Nil, SetConfig(modify) :: acc) + go(sts, Nil, SetConfig(modify)(directive.toLoc) :: acc) case Directive(Ident(name), _) :: sts => raise(ErrorReport( msg"Unknown directive '#${name}'" -> sts.headOption.flatMap(_.toLoc) :: Nil, @@ -2536,7 +2536,7 @@ extends Importer: case Tup(ps) => def go(ps: Ls[Tree], acc: Ls[Param], ctx: Ctx, flags: ParamListFlags): (ParamList, Ctx) = ps match - case Nil => (ParamList(flags, acc.reverse, N).withLocOf(t), ctx) + case Nil => (ParamList(flags, acc.reverse, N)(t.toLoc), ctx) case hd :: tl => val isCtxParam = hd.isModified(Ins) val inUsing = flags.ctx || isCtxParam @@ -2552,7 +2552,7 @@ extends Importer: if spd is SpreadKind.Lazy then raise(ErrorReport(msg"Lazy spread parameters not allowed." -> hd.toLoc :: Nil)) if tl.isEmpty then - (ParamList(flags, acc.reverse, S(p)).withLocOf(t), newCtx) + (ParamList(flags, acc.reverse, S(p))(t.toLoc), newCtx) else raise(ErrorReport(msg"Spread parameters must be the last in the parameter list." -> hd.toLoc :: Nil)) go(tl, p :: acc, newCtx, newFlags) @@ -2563,7 +2563,7 @@ extends Importer: raise: ErrorReport: msg"Expected a parameter list (a tuple of parameters), but found ${t.describe}" -> t.toLoc :: Nil - (ParamList(ParamListFlags.empty, Nil, N).withLocOf(t), ctx) + (ParamList(ParamListFlags.empty, Nil, N)(t.toLoc), ctx) def ident(id: Ident)(using Ctx): Ctxl[Opt[Term]] = ctx.get(id.name) match case S(elem) => S(elem.ref(id)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala index 41cc73f1e4..8aa5213b93 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Importer.scala @@ -19,7 +19,7 @@ class Importer: import tl.* - def importPath(path: Str, alias: Opt[syntax.Tree.Ident])(using cfg: Config): Import = + def importPath(path: Str, alias: Opt[syntax.Tree.Ident])(loc: Opt[Loc])(using cfg: Config): Import = // log(s"pwd: ${os.pwd}") // log(s"wd: ${wd}") @@ -42,7 +42,7 @@ class Importer: file.ext match case "mjs" | "js" => - Import(sym, file.toString, file) + Import(sym, file.toString, file)(loc) case "mls" => def reportCycle(files: Ls[io.Path]): Import = @@ -50,7 +50,7 @@ class Importer: ErrorReport: msg"Circular imports of `mls` files are not yet supported" -> N :: files.map(f => msg" importing ${f.toString}" -> N) - Import(sym, path, file) + Import(sym, path, file)(loc) if cctx.beingCompiled.contains(file) then reportCycle(cctx.allFilesBeingImported :+ file) @@ -65,13 +65,13 @@ class Importer: VarSymbol(alias, erasedType = N) val jsFile = file.up / io.RelPath(file.baseName + ".mjs") - Import(sym, jsFile.toString, jsFile) + Import(sym, jsFile.toString, jsFile)(loc) case _ => if file.ext =/= "mls" then raise: ErrorReport(msg"Unsupported file extension: ${file.ext}" -> N :: Nil) - Import(sym, path, file) + Import(sym, path, file)(loc) else - Import(sym, path, file) + Import(sym, path, file)(loc) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 924f6aa611..947c56a25b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -18,22 +18,27 @@ import hkmc2.Message.MessageContext final case class QuantVar(sym: VarSymbol, ub: Opt[Term], lb: Opt[Term]) -enum Annot extends AutoLocated: - case Untyped - case Modifier(mod: Keyword) - case Trm(trm: Term) +/** Each annotation occurrence owns its source span, including marker annotations. + * The auxiliary location does not affect equality or matching by annotation kind. + */ +enum Annot extends Located: + val toLoc: Opt[Loc] + + case Untyped()(val toLoc: Opt[Loc]) + case Modifier(mod: Keyword)(val toLoc: Opt[Loc]) + case Trm(trm: Term)(val toLoc: Opt[Loc]) // NOTE: The presence of TailRec and TailCall annotations does not affect whether a function is optimized or not; // it only affects whether a warning is thrown if the function/call is not actually tail-recursive. - case TailRec - case TailCall - case Inline - case NoInline - case Generator - case Async - case RaiseEffects + case TailRec()(val toLoc: Opt[Loc]) + case TailCall()(val toLoc: Opt[Loc]) + case Inline()(val toLoc: Opt[Loc]) + case NoInline()(val toLoc: Opt[Loc]) + case Generator()(val toLoc: Opt[Loc]) + case Async()(val toLoc: Opt[Loc]) + case RaiseEffects()(val toLoc: Opt[Loc]) // Whether the function is guaranteed to not raise effects. - case Pure - case Config(modify: hkmc2.Config => hkmc2.Config) + case Pure()(val toLoc: Opt[Loc]) + case Config(modify: hkmc2.Config => hkmc2.Config)(val toLoc: Opt[Loc]) // Marks if a function or lambda is one-shot, i.e. called at most once. // Functions with multiple parameter lists are considered here as a chain of // function values. `whichParamList` is the zero-based index of the parameter @@ -44,7 +49,7 @@ enum Annot extends AutoLocated: // each function value produced by `f(a)` is one-shot; // - its list of annotations containing both `Affine(0)` and `Affine(1)` says that // `f` is one-shot and each function value produced by `f(a)` is also one-shot. - case Affine(whichParamList: Int) + case Affine(whichParamList: Int)(val toLoc: Opt[Loc]) def symbol: Opt[Symbol] = this match case Trm(trm) => trm.symbol @@ -52,48 +57,31 @@ enum Annot extends AutoLocated: def subTerms: Vector[Term] = this match case Trm(trm) => Vector.single(trm) - case _: Modifier | Untyped | TailRec | TailCall | Inline | NoInline - | Generator | Async | RaiseEffects | Pure | _: Config | _: Affine => Vector.empty - - def children: Vector[Located] = this match - case Trm(trm) => Vector.single(trm) - // case Modifier(kw) => Vector.single(kw) // TODO: make `kw` a `Keywrd` - case _: Modifier | Untyped | TailRec | TailCall | Inline | NoInline - | Generator | Async | RaiseEffects | Pure | _: Config | _: Affine => Vector.empty + case _: Modifier | Untyped() | TailRec() | TailCall() | Inline() | NoInline() + | Generator() | Async() | RaiseEffects() | Pure() | _: Config | _: Affine => Vector.empty def show(using Scope, ShowCfg, Raise): Document = this match - case Untyped => doc"@untyped" - case Inline => doc"@inline" - case NoInline => doc"@noInline" - case Generator => doc"@generator" - case Async => doc"@async" - case RaiseEffects => doc"@raiseEffects" - case TailRec => doc"@tailrec" - case TailCall => doc"@tailcall" + case Untyped() => doc"@untyped" + case Inline() => doc"@inline" + case NoInline() => doc"@noInline" + case Generator() => doc"@generator" + case Async() => doc"@async" + case RaiseEffects() => doc"@raiseEffects" + case TailRec() => doc"@tailrec" + case TailCall() => doc"@tailcall" case Affine(n) => doc"@affine($n)" case Modifier(mod) => doc"@${mod.name}" - case Pure => doc"@pure" + case Pure() => doc"@pure" case Trm(trm) => doc"@${trm.show}" case Config(_) => doc"@config(...)" def mkClone(using State): Annot = this match - case Untyped => Untyped - case Modifier(mod) => Modifier(mod) - case Trm(trm) => Trm(trm.mkClone) - case TailRec => TailRec - case TailCall => TailCall - case Inline => Inline - case NoInline => NoInline - case Generator => Generator - case Async => Async - case RaiseEffects => RaiseEffects - case Pure => Pure - case c: Config => c - case a: Affine => a + case Trm(trm) => Trm(trm.mkClone)(toLoc) + case _ => this object Annot: - val Private = Modifier(Keyword.`private`) + val Private = Modifier(Keyword.`private`)(N) /** The `declare` modifier in `annotations`, if present. */ def declareModifierOf(annotations: Ls[Annot]): Opt[Annot.Modifier] = annotations.collectFirst: @@ -651,11 +639,11 @@ sealed trait Statement extends Located, ProductWithExtraInfo: def mkClone(using State): Statement = this match case t: Term => lastWords(s"overridden implementation") case d: Definition => ??? - case imp: Import => Import(imp.sym, imp.str, imp.file) - case LetDecl(sym, annotations) => LetDecl(sym, annotations.map(_.mkClone)) + case imp: Import => Import(imp.sym, imp.str, imp.file)(imp.toLoc) + case LetDecl(sym, annotations) => LetDecl(sym, annotations.map(_.mkClone))(toLoc) case RcdField(field, rhs) => RcdField(field.mkClone, rhs.mkClone) case RcdSpread(rcd) => RcdSpread(rcd.mkClone) - case DefineVar(sym, rhs) => DefineVar(sym, rhs.mkClone) + case DefineVar(sym, rhs) => DefineVar(sym, rhs.mkClone)(toLoc) case sc: SetConfig => sc def describe: Str = @@ -984,16 +972,16 @@ sealed trait Statement extends Located, ProductWithExtraInfo: case LeadingDotSel(nme) => s"_?_.${nme.name}" case SetConfig(_) => "#config(...)" -final case class LetDecl(sym: LocalVarSymbol | TermSymbol, annotations: Ls[Annot]) extends Statement, AutoLocated +final case class LetDecl(sym: LocalVarSymbol | TermSymbol, annotations: Ls[Annot])(val toLoc: Opt[Loc]) extends Statement final case class RcdField(field: Term, rhs: Term) extends Statement, AutoLocated final case class RcdSpread(rcd: Term) extends Statement, AutoLocated -final case class DefineVar(sym: LocalSymbol | TermSymbol, rhs: Term) extends Statement, AutoLocated +final case class DefineVar(sym: LocalSymbol | TermSymbol, rhs: Term)(val toLoc: Opt[Loc]) extends Statement /** A global configuration change directive (`#config(...)`). * Records a function that modifies the current compiler configuration. */ -final case class SetConfig(modify: hkmc2.Config => hkmc2.Config) extends Statement, AutoLocated: +final case class SetConfig(modify: hkmc2.Config => hkmc2.Config)(val toLoc: Opt[Loc]) extends Statement: override def toString: String = "#config(...)" enum Visibility: @@ -1069,7 +1057,7 @@ final case class TermDefinition( .getOrElse(Visibility.Public) lazy val mayRaiseEffects: Bool = annotations.forall: - case Annot.Pure => false + case Annot.Pure() => false case _ => true def extraAnnotations: Ls[Annot] = annotations.filter: case Annot.Modifier(Keyword.`declare` | Keyword.`abstract`) => false @@ -1143,7 +1131,7 @@ end ObjBody * in which case it is a `BlockMemberSymbol` when importing files explicitly * and a `TermSymbol` when the import is made implicitly by the compiler (eg, importing "Predef"). * Note that the `file` Path may not represent a real file; eg when importing "fs". */ -case class Import(sym: ImportSymbol, str: Str, file: io.Path) extends Statement, AutoLocated +case class Import(sym: ImportSymbol, str: Str, file: io.Path)(val toLoc: Opt[Loc]) extends Statement /** Declaration spans come from the syntax that introduced them, never from semantic @@ -1433,8 +1421,7 @@ final case class Param(flags: FldFlags, sym: VarSymbol, sign: Opt[Term], modulef def showDbg(using DebugPrinter): Str = flags.show(true) + sym.showDbg + sign.fold("")(": " + _.showDbg) final case class ParamList(flags: ParamListFlags, params: Ls[Param], restParam: Opt[Param]) -extends AutoLocated: - override protected def children: Vector[Located] = params.toVector ++ restParam +(val toLoc: Opt[Loc]) extends Located: def foreach(f: Param => Unit): Unit = (params.iterator ++ restParam).foreach(f) def paramCountLB: Int = params.length def paramCountUB: Bool = restParam.isEmpty @@ -1452,8 +1439,8 @@ extends AutoLocated: def showDbg(using DebugPrinter): Str = flags.showDbg + (params.map(_.showDbg) ++ restParam.toList.map("..." + _.showDbg)).mkString("(", ", ", ")") object PlainParamList: - def apply(params: Ls[Param]) = - ParamList(ParamListFlags.empty, params, N) + def apply(params: Ls[Param])(toLoc: Opt[Loc]) = + ParamList(ParamListFlags.empty, params, N)(toLoc) def unapply(pl: ParamList): Opt[Ls[Param]] = pl match case ParamList(ParamListFlags.empty, params, N) => S(params) case _ => N 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 0916726691..40c6223142 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala @@ -348,7 +348,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C Case.Cls(ctorSym, st) -> lowerSplit(tail, cont) case (param, arg) :: args => val (cse, blk) = mkArgs(args) - (cse, Assign(arg, Select(sr, new Tree.Ident(param.id.name).withLocOf(arg))(S(param))(false), blk)) + (cse, Assign(arg, Select(sr, new Tree.Ident(param.id.name).withLocOf(arg))(S(param), N)(false), blk)) mkMatch(mkArgs(clsParams.iterator.zip(args).toList)) symbol match case cls: ClassSymbol if ctx.builtins.virtualClasses contains cls => @@ -370,12 +370,12 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C for (_, s) <- entries do LoweringCtx.loweringCtx.collectScopedSym(s) val objectSym = ctx.builtins.Object mkMatch( // checking that we have an object - Case.Cls(objectSym, Select(State.globalThisSymbol.asThis, Tree.Ident(objectSym.nme))(S(objectSym))(false)), + Case.Cls(objectSym, Select(State.globalThisSymbol.asThis, Tree.Ident(objectSym.nme))(S(objectSym), N)(false)), entries.foldRight(lowerSplit(tail, cont)): case ((fieldName, fieldSymbol), blk) => mkMatch( Case.Field(fieldName, safe = true), // we know we have an object, no need to check again - Assign(fieldSymbol, Select(sr, fieldName)(N)(false), blk) + Assign(fieldSymbol, Select(sr, fieldName)(N, N)(false), blk) ) ) case Split.Else(els) => @@ -425,8 +425,8 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C * match failure in the future. */ private def throwMatchErrorBlock = - Throw(Instantiate(mut = false, Select(State.globalThisSymbol.asThis, Tree.Ident("Error"))(S(ctx.builtins.Error))(false), - (Value.Lit(syntax.Tree.StrLit("match error")).asArg :: Nil) :: Nil)(InstantiateMetadata.empty)) // TODO add failed-match scrutinee info + Throw(Instantiate(mut = false, Select(State.globalThisSymbol.asThis, Tree.Ident("Error"))(S(ctx.builtins.Error), N)(false), + (Value.Lit(syntax.Tree.StrLit("match error"))(N).asArg :: Nil) :: Nil)(InstantiateMetadata.empty, N)) // TODO add failed-match scrutinee info /** Gives a lowering temp the join of the representations of the results stored into it. * @@ -509,7 +509,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C // 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(f.asMemberRef(tSym), Nil ne_:: Nil)(CallMetadata.mlsFunWithEffect)) + then Return(Call(f.asMemberRef(tSym), Nil ne_:: Nil)(CallMetadata.mlsFunWithEffect, N)) else Continue(loopLabel) val cont = form match @@ -546,14 +546,14 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C outerCtx.collectScopedSym(loopResult) outerCtx.collectScopedSym(isReturned) val loopEnd: Path = - Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("LoopEnd"))(S(State.loopEndSymbol))(false) + Select(State.runtimeSymbol.asSimpleRef, Tree.Ident("LoopEnd"))(S(State.loopEndSymbol), N)(false) val blk = blockBuilder - .define(FunDefn(N, f, tSym, PlainParamList(Nil) :: Nil, Begin(body, Return(loopEnd)))(configOverride = N, annotations = Nil)) - .assign(loopResult, Call(f.asMemberRef(tSym), Nil ne_:: Nil)(CallMetadata.mlsFunWithEffect)) + .define(FunDefn(N, f, tSym, PlainParamList(Nil)(N) :: Nil, Begin(body, Return(loopEnd)))(configOverride = N, annotations = Nil)) + .assign(loopResult, Call(f.asMemberRef(tSym), Nil ne_:: Nil)(CallMetadata.mlsFunWithEffect, N)) if summon[LoweringCtx].mayRet then blk .assign(isReturned, Call(State.builtinOpsMap("!==").asSimpleRef, - (loopResult.asPath.asArg :: loopEnd.asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun)) + (loopResult.asPath.asArg :: loopEnd.asArg :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N)) .ifthen(isReturned.asSimpleRef, Case.Lit(Tree.BoolLit(true)), Return(loopResult.asSimpleRef), N diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala index eeaaca141c..3484de161d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala @@ -34,7 +34,7 @@ trait TermSynthesizer(using State): Split.Let(sym, term, inner) protected final def param = Param.simple - protected final def paramList(params: Param*) = PlainParamList(params.toList) + protected final def paramList(params: Param*) = PlainParamList(params.toList)(N) private lazy val runtimeRef: Term.Ref = State.runtimeSymbol.ref().resolve diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala index 8a0f127bd8..8aecbdfbcb 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala @@ -218,7 +218,7 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter val consequent = Split.Else(makeResult(fieldSymbol)) val branch = Branch(scrutinee.safeRef, fieldTest, consequent) SynthIf(branch ~: Split.Else(subScrutinee.default)) - LetDecl(subScrutinee.symbol, Nil) :: DefineVar(subScrutinee.symbol, result) :: Nil + LetDecl(subScrutinee.symbol, Nil)(N) :: DefineVar(subScrutinee.symbol, result)(N) :: Nil .toList // For each pattern, we compile a split and bind the result to a variable. // The variable will be a field of the output record. @@ -235,7 +235,7 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter else makeMatchSuccess(outputSymbol.use, nullifyEmptyBindings(bindings.use)), alternative = Split.Else(emptyMatchResult("topmost"))) val test = SynthIf(split) - (DefineVar(symbol, test) :: LetDecl(symbol, Nil) :: stmts, (label, symbol.safeRef) :: results) + (DefineVar(symbol, test)(N) :: LetDecl(symbol, Nil)(N) :: stmts, (label, symbol.safeRef) :: results) // Materialize the matcher's final return value. Singleton matchers return // their only field directly; multi-label matchers still return a record. val resultTerm = resultTerms.reverse match @@ -528,8 +528,8 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter // then `term`. val letBindings = pattern.symbols.flatMap: symbol => val termSymbol = correspondence(symbol) - LetDecl(termSymbol, Nil) :: - DefineVar(termSymbol, sel(bindingsSymbol.safeRef, termSymbol.name)) :: Nil + LetDecl(termSymbol, Nil)(N) :: + DefineVar(termSymbol, sel(bindingsSymbol.safeRef, termSymbol.name))(N) :: Nil val makeSplit = completePattern(pattern, scrutinee, subScrutinees, Nil) (makeConsequent, alternative) => Split.Let( sym = transformSymbol, diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala index 077ad86c69..1437171f44 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/FixedPointCompiler.scala @@ -762,11 +762,11 @@ class FixedPointCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynt matchers.flatMap((_, stepImpls, postMatcherOpt) => stepImpls ::: postMatcherOpt.fold(Nil)(_._2) ).flatMap: (symbol, params, body) => - LetDecl(symbol, Nil) :: DefineVar(symbol, Term.Lam(params, body)) :: Nil + LetDecl(symbol, Nil)(N) :: DefineVar(symbol, Term.Lam(params, body))(N) :: Nil ::: List( - LetDecl(phaseSymbol, Nil), DefineVar(phaseSymbol, int(0)), - LetDecl(focusSymbol, Nil), DefineVar(focusSymbol, inputSymbol.safeRef), - LetDecl(failPhaseSymbol, Nil), DefineVar(failPhaseSymbol, int(-1))) + LetDecl(phaseSymbol, Nil)(N), DefineVar(phaseSymbol, int(0))(N), + LetDecl(focusSymbol, Nil)(N), DefineVar(focusSymbol, inputSymbol.safeRef)(N), + LetDecl(failPhaseSymbol, Nil)(N), DefineVar(failPhaseSymbol, int(-1))(N)) Machine(paramList(param(inputSymbol)), prelude, loop, result) @@ -965,14 +965,14 @@ class FixedPointCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynt val prelude = (redexImpls ::: sideImpls ::: postMatcherOpt.fold(Nil)(_._2)).flatMap: (symbol, params, body) => - LetDecl(symbol, Nil) :: DefineVar(symbol, Term.Lam(params, body)) :: Nil + LetDecl(symbol, Nil)(N) :: DefineVar(symbol, Term.Lam(params, body))(N) :: Nil ::: List( - LetDecl(modeSymbol, Nil), DefineVar(modeSymbol, int(ModeFind)), - LetDecl(focusSymbol, Nil), DefineVar(focusSymbol, inputSymbol.safeRef), - LetDecl(stackSymbol, Nil), DefineVar(stackSymbol, `null`), - LetDecl(resultSymbol, Nil), DefineVar(resultSymbol, `null`)) + LetDecl(modeSymbol, Nil)(N), DefineVar(modeSymbol, int(ModeFind))(N), + LetDecl(focusSymbol, Nil)(N), DefineVar(focusSymbol, inputSymbol.safeRef)(N), + LetDecl(stackSymbol, Nil)(N), DefineVar(stackSymbol, `null`)(N), + LetDecl(resultSymbol, Nil)(N), DefineVar(resultSymbol, `null`)(N)) ::: (if requireProgress then - LetDecl(progressedSymbol, Nil) :: DefineVar(progressedSymbol, bool(false)) :: Nil + LetDecl(progressedSymbol, Nil)(N) :: DefineVar(progressedSymbol, bool(false))(N) :: Nil else Nil) // Succeed with the normal form, post-processed by the trailing diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala index 221c699958..6b44c7b977 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/SplitCompiler.scala @@ -802,7 +802,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz // consequence split. (makeConsequent, alternative) => Split.Let( sym = lambdaSymbol, - term = Term.Lam(PlainParamList(params), transform.mkClone), + term = Term.Lam(PlainParamList(params)(N), transform.mkClone), // Declare the lambda function at the outermost level. Even if there // are multiple disjunctions in the consequent, we will not need to // repeat the `transform` term. @@ -1075,7 +1075,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz val lambdaSymbol = new TempSymbol(N, erasedType = N, "transform") (makeConsequent, alternative) => Split.Let( sym = lambdaSymbol, - term = Term.Lam(PlainParamList(params), transform), + term = Term.Lam(PlainParamList(params)(N), transform), tail = make( // Note that the output is not used. Semantically, the `transform` // term can only access the matched values by bindings. @@ -1250,7 +1250,7 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz val patternInputs = patternParameters.map(p => p.copy(flags = FldFlags.empty)(p.toLoc)) // The last parameter is the scrutinee. val scrutParam = Param.simple(scrut) - val ps = PlainParamList(patternInputs :+ scrutParam) + val ps = PlainParamList(patternInputs :+ scrutParam)(N) (sym, ps, topmost) /** Translate a list of extractor/matching functions for the given pattern. @@ -1318,11 +1318,11 @@ class SplitCompiler(using tl: TL)(using State, Ctx, Raise) extends TermSynthesiz topmost: Split ): Ls[Statement] = val fieldSymbol = TempSymbol(N, erasedType = N, name) - val decl = LetDecl(fieldSymbol, Nil) + val decl = LetDecl(fieldSymbol, Nil)(N) val param = Param.simple(scrut) - val paramList = PlainParamList(param :: Nil) + val paramList = PlainParamList(param :: Nil)(N) val lambda = Term.Lam(paramList, Term.SynthIf(topmost)) - val defineVar = DefineVar(fieldSymbol, lambda) + val defineVar = DefineVar(fieldSymbol, lambda)(N) val field = RcdField(str(name), fieldSymbol.safeRef) decl :: defineVar :: field :: Nil diff --git a/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala b/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala index fdd206d686..8b96641683 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala @@ -128,7 +128,7 @@ object DebugPrinter: val emptyValues: Set[Any] = Set( None, Nil, Vector.empty, - semantics.PlainParamList(Nil), + semantics.PlainParamList(Nil)(N), ParamListFlags.empty, TermDefFlags.empty, FldFlags.empty, diff --git a/hkmc2/shared/src/test/mlscript/block-staging/Functions.mls b/hkmc2/shared/src/test/mlscript/block-staging/Functions.mls index 9f749b55ab..18b113863a 100644 --- a/hkmc2/shared/src/test/mlscript/block-staging/Functions.mls +++ b/hkmc2/shared/src/test/mlscript/block-staging/Functions.mls @@ -261,7 +261,7 @@ staged module A with //│ ═══[COMPILATION ERROR] Field assignment is not supported in staged modules: a //│ ╔══[COMPILATION ERROR] Other Results not supported in staged module: class hkmc2.codegen.Record //│ ║ l.260: fun g() = {1 : 2} -//│ ╙── ^ +//│ ╙── ^^^^^ //│ > fun ctor_() = //│ > x = C(1) //│ ═══[RUNTIME ERROR] Error: MLscript call unexpectedly returned `undefined`, the forbidden value. diff --git a/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls b/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls index d368b46382..8cc52dad51 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls @@ -117,10 +117,10 @@ bar([foo].0) bar([foo, x => x].1) //│ ╔══[COMPILATION ERROR] Cannot determine if 0 is a function. //│ ║ l.116: bar([foo].0) -//│ ╙── ^^ +//│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Cannot determine if 1 is a function. //│ ║ l.117: bar([foo, x => x].1) -//│ ╙── ^^ +//│ ╙── ^^^^^^^^^^^^^^^ //│ = 0 @@ -135,7 +135,7 @@ bar([foo, x => x].(i)) [foo, x => x].(i)(0) //│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. //│ ║ l.135: [foo, x => x].(i)(0) -//│ ╙── ^ +//│ ╙── ^^^^^^^^^^^^^^^^ class Foo() with diff --git a/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls new file mode 100644 index 0000000000..82775ba8e7 --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls @@ -0,0 +1,43 @@ +:. +:js +open annotations + +// Marker annotations have separate locations for each occurrence. +:w +@inline +class First +@inline +class Second +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.7: @inline +//│ ╙── ^^^^^^^ +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.9: @inline +//│ ╙── ^^^^^^^ + +// Annotations on let bindings point to the annotation occurrence. +:w +@tailrec +let value = 1 +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.20: @tailrec +//│ ╙── ^^^^^^^^ +//│ value = 1 + +// Annotations on bodyless functions retain their own locations. +:w +@inline +fun abstractFunction() +//│ ╔══[WARNING] Only functions with a body may be marked as @inline. +//│ ║ l.29: @inline +//│ ╙── ^^^^^^^ + +// This error is emitted from lowered IR after the prefix has become a temporary. +:ftc +:ge +let index = 0 +[(x => x)].(index)(1) +//│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. +//│ ║ l.39: [(x => x)].(index)(1) +//│ ╙── ^^^^^^^^^^^^^^^^^ +//│ index = 0 diff --git a/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls b/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls deleted file mode 100644 index de1cdffb91..0000000000 --- a/hkmc2/shared/src/test/mlscript/newres/DeclarationLocations.mls +++ /dev/null @@ -1,95 +0,0 @@ -:. - -// An empty declaration still has a source span. -:e -module Empty -Empty.missing -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.6: Empty.missing -//│ ║ ^^^^^^^^^^^^^ -//│ ╟── Module 'Empty' does not contain member 'missing' -//│ ║ l.5: module Empty -//│ ╙── ^^^^^ -//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. - -// A member annotation must not become the enclosing module's location. -:e -module Container with - declare fun text(): Str -Container.missing -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.19: Container.missing -//│ ║ ^^^^^^^^^^^^^^^^^ -//│ ╟── Module 'Container' does not contain member 'missing' -//│ ║ l.17: module Container with -//│ ╙── ^^^^^^^^^ -//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. - -// Nested declarations retain their own spans. -:e -module Outer with - module Inner with - declare fun text(): Str -Outer.Inner.missing -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.33: Outer.Inner.missing -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ╟── Module 'Inner' does not contain member 'missing' -//│ ║ l.31: module Inner with -//│ ╙── ^^^^^ -//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. - -// A nominal annotation continues to point at the use of the type. -:e -class Base with - declare fun text(): Str -fun hidden(value: Base) = value.missing -hidden(new Base) -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.46: fun hidden(value: Base) = value.missing -//│ ║ ^^^^^^^^^^^^^ -//│ ╟── Value of type 'Base' does not contain member 'missing' -//│ ║ l.46: fun hidden(value: Base) = value.missing -//│ ╙── ^^^^ -//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. - -// The receiver inside a class uses the declaration, including its name and body. -:e -class Receiver with - declare fun text(): Str - fun inspect() = this.missing -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.60: fun inspect() = this.missing -//│ ║ ^^^^^^^^^^^^ -//│ ╟── Class definition 'Receiver' does not contain member 'missing' -//│ ║ l.58: class Receiver with -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.59: declare fun text(): Str -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.60: fun inspect() = this.missing -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -// Parameterized classes likewise use the written declaration, not only its fields. -:e -class Parameterized(value: Int) with - fun inspect() = this.missing -//│ ╔══[COMPILATION ERROR] Resolution error in selection -//│ ║ l.75: fun inspect() = this.missing -//│ ║ ^^^^^^^^^^^^ -//│ ╟── Class definition 'Parameterized' does not contain member 'missing' -//│ ║ l.74: class Parameterized(value: Int) with -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.75: fun inspect() = this.missing -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -// A class without fields has a declaration location even in pattern diagnostics. -:e -class Empty -if new Empty is Empty(_) then 1 else 0 -//│ ╔══[COMPILATION ERROR] Resolution error in constructor -//│ ║ l.88: if new Empty is Empty(_) then 1 else 0 -//│ ║ ^^^^^^^^ -//│ ╟── Class definition 'Empty' does not take pattern arguments. -//│ ║ l.87: class Empty -//│ ╙── ^^^^^^^^^^^ -//│ = 0 diff --git a/hkmc2/shared/src/test/mlscript/tailrec/Annots.mls b/hkmc2/shared/src/test/mlscript/tailrec/Annots.mls index 30e09e83cf..70190a6379 100644 --- a/hkmc2/shared/src/test/mlscript/tailrec/Annots.mls +++ b/hkmc2/shared/src/test/mlscript/tailrec/Annots.mls @@ -5,18 +5,24 @@ open annotations :w @tailrec fun f -//│ ═══[WARNING] Only functions with a body may be marked as @tailrec. +//│ ╔══[WARNING] Only functions with a body may be marked as @tailrec. +//│ ║ l.6: @tailrec +//│ ╙── ^^^^^^^^ :w @tailrec class A -//│ ═══[WARNING] This annotation has no effect. +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.13: @tailrec +//│ ╙── ^^^^^^^^ :w @tailrec 2 + 2 //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.20: @tailrec 2 + 2 +//│ ║ ^^^^^^^^ //│ ╟── This annotation is not supported on application terms. -//│ ║ l.16: @tailrec 2 + 2 +//│ ║ l.20: @tailrec 2 + 2 //│ ╙── ^^^^^ //│ = 4 @@ -27,7 +33,9 @@ fun g = f :w @tailcall fun g = 2 -//│ ═══[WARNING] This annotation has no effect. +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.34: @tailcall +//│ ╙── ^^^^^^^^^ fun test = @tailcall test @@ -37,8 +45,10 @@ let f = 0 fun test = @tailcall f //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.46: @tailcall f +//│ ║ ^^^^^^^^^ //│ ╟── This annotation is not supported on reference terms. -//│ ║ l.38: @tailcall f +//│ ║ l.46: @tailcall f //│ ╙── ^ //│ f = 0 @@ -48,14 +58,16 @@ class A with fun f() = g() fun g() = f() //│ ╔══[COMPILATION ERROR] Class methods may not yet be marked @tailrec. -//│ ║ l.48: fun f() = g() +//│ ║ l.58: fun f() = g() //│ ╙── ^ :w class A with @tailcall fun f = 2 -//│ ═══[WARNING] This annotation has no effect. +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.66: @tailcall +//│ ╙── ^^^^^^^^^ :todo class A @@ -63,7 +75,7 @@ module A with @tailrec fun f = 2 //│ ╔══[WARNING] This function is marked @tailrec but has no direct self-recursion. -//│ ║ l.64: fun f = 2 +//│ ║ l.76: fun f = 2 //│ ╙── ^ :w @@ -71,7 +83,9 @@ class A module A with @tailcall fun f = 2 -//│ ═══[WARNING] This annotation has no effect. +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.84: @tailcall +//│ ╙── ^^^^^^^^^ :w @@ -80,12 +94,16 @@ module A with handle h = Object with @tailcall fun f()(r) = 2 -//│ ═══[WARNING] This annotation has no effect. +//│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.95: @tailcall +//│ ╙── ^^^^^^^^^ :w fun test = @tailcall 1 + 2 //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.103: @tailcall 1 + 2 +//│ ║ ^^^^^^^^^ //│ ╟── The @tailcall annotation has no effect on calls to built-in symbols. -//│ ║ l.87: @tailcall 1 + 2 -//│ ╙── ^^^^^ +//│ ║ l.103: @tailcall 1 + 2 +//│ ╙── ^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls index d2917ad7be..fd4e9f3807 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls @@ -81,7 +81,9 @@ fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) // not used as an immediate (or just do it away entirely). :ge wasm.i32.const(-1) -//│ ═══[COMPILATION ERROR] Wasm intrinsic 'i32.const' expects an integer literal immediate +//│ ╔══[COMPILATION ERROR] Wasm intrinsic 'i32.const' expects an integer literal immediate +//│ ║ l.88: wasm.i32.const(-1) +//│ ╙── ^^ // The remaining arithmetic instructions. Each runs on operands whose result identifies it - `-1`, `42`, `3` diff --git a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala index 3b1a552bed..b20bcb78ec 100644 --- a/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala +++ b/hkmc2DiffTests/src/test/scala/hkmc2/JSBackendDiffMaker.scala @@ -304,7 +304,7 @@ abstract class JSBackendDiffMaker extends MLsDiffMaker: NoSymbol, Call( Elaborator.State.runtimeSymbol.asSimpleRef.selSN("printRaw"), - (Arg(N, sym.asPath) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun), + (Arg(N, sym.asPath) :: Nil) ne_:: Nil)(CallMetadata.defaultMlsFun, N), End()) val je = nestedScp.givenIn: jsb.block(le, endSemi = false) From 1c7e8b0d92b749dfe51e19f649eacfe11f3895e7 Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 29 Sep 2026 22:34:52 +0800 Subject: [PATCH 3/5] Preserve complete declaration spans while unwrapping annotations --- .../scala/hkmc2/semantics/Elaborator.scala | 98 ++++++++++--------- .../src/test/mlscript/objbuf/BasicsObjBuf.mls | 26 +++-- .../test/mlscript/ups/syntax/WrongArity.mls | 47 +++++++-- 3 files changed, 110 insertions(+), 61 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 6b1e73abde..b5e6350344 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -1762,7 +1762,11 @@ extends Importer: // * @param funs: // * While elaborating a block, we move all function definitions to the top (similar to JS function semantics) @tailrec - def go(sts: Ls[Tree], annotations: Ls[Annot], acc: Ls[Statement]): Ctxl[(Blk | Rcd, Ctx)] = + def go(sts: Ls[Tree], annotations: Ls[Annot], acc: Ls[Statement])(sourceLoc: Opt[Loc]): Ctxl[(Blk | Rcd, Ctx)] = + // Annotation wrappers belong to the declaration's source span even when the + // annotation itself is invalid. Preserve the outer syntax while unwrapping; + // advancing to another statement starts a fresh span. + val statementLoc = sourceLoc.orElse(sts.headOption.flatMap(_.toLoc)) /** Call this function when the following term cannot be annotated. */ def reportUnusedAnnotations: Unit = if annotations.nonEmpty then raise: WarningReport: @@ -1780,16 +1784,16 @@ extends Importer: (mkBlk(acc, N, hasResult), ctx) case Constructor(Block(ctors)) :: sts => // TODO properly handle (it currently desugars to sibling classes) - go(sts, annotations, acc) + go(sts, annotations, acc)(N) case (ctorParams @ Constructor(ConstructorParamDecl(_))) :: sts => // constructor(x, y) or constructor(x, y)(u, v) syntax: params are extracted during class elaboration ctx.getOuter match case S(_: ClassSymbol) => - go(sts, annotations, acc) + go(sts, annotations, acc)(N) case _ => raise(ErrorReport(msg"'constructor(...)' declarations are only allowed in class bodies" -> ctorParams.toLoc :: Nil)) - go(sts, annotations, acc) + go(sts, annotations, acc)(N) case Open(bod) :: sts => reportUnusedAnnotations bod match @@ -1805,7 +1809,7 @@ extends Importer: raise(ErrorReport(msg"Illegal 'open' statement shape." -> bod.toLoc :: Nil)) N match - case N => go(sts, annotations, acc) + case N => go(sts, annotations, acc)(N) case S((base, importedTrees)) => base match case baseId: Ident => @@ -1831,13 +1835,13 @@ extends Importer: raise(ErrorReport(msg"Illegal 'open' statement element." -> t.toLoc :: Nil)) Nil (ctx elem_++ importedNames).givenIn: - go(sts, Nil, acc) + go(sts, Nil, acc)(N) case N => raise(ErrorReport(msg"Name not found: ${baseId.name}" -> baseId.toLoc :: Nil)) - go(sts, Nil, acc) + go(sts, Nil, acc)(N) case _ => raise(ErrorReport(msg"Illegal 'open' statement base." -> base.toLoc :: Nil)) - go(sts, Nil, acc) + go(sts, Nil, acc)(N) case (m @ PrefixApp(Keywrd(Keyword.`import`), arg)) :: sts => reportUnusedAnnotations val pathAndAlias: Opt[(Tree, Opt[Ident])] = arg match @@ -1851,7 +1855,7 @@ extends Importer: case pathArg => S((pathArg, N)) val (newCtx, newAcc) = pathAndAlias match case S((StrLit(path), alias)) => - val stmt = importPath(path, alias)(m.toLoc) + val stmt = importPath(path, alias)(statementLoc) (ctx + (stmt.sym.nme -> stmt.sym), stmt :: acc) case S((pathArg, _)) => @@ -1862,11 +1866,11 @@ extends Importer: case N => // errors have been reported above. (ctx, acc) newCtx.givenIn: - go(sts, Nil, newAcc) + go(sts, Nil, newAcc)(N) case Spread(Keywrd(Keyword.`...`), S(body)) :: sts => reportUnusedAnnotations - go(sts, Nil, RcdSpread(term(body)) :: acc) + go(sts, Nil, RcdSpread(term(body)) :: acc)(N) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) :: sts => var newCtx = ctx val (rlhs, rhs_t) = rhs match @@ -1895,7 +1899,7 @@ extends Importer: raise(ErrorReport(msg"Unexpected record key shape." -> rlhs.toLoc :: Nil)) RcdField(Term.Error().withLocOf(rlhs), rhs_t) :: acc newCtx.givenIn: - go(sts, Nil, newAcc) + go(sts, Nil, newAcc)(N) case (hd @ LetLike(kw @ Keywrd(`let`), Apps(id: Ident, tups), rhso, N)) :: sts if tups.isEmpty || id.name.headOption.exists(_.isLower) => val sym = @@ -1908,16 +1912,16 @@ extends Importer: case S(rhs) => val rrhs = tups.foldRight(rhs): InfixApp(_, Keywrd(Keyword.`=>`), _) - mkLetBinding(sym, term(rrhs), annotations)(hd.toLoc) reverse_::: acc + mkLetBinding(sym, term(rrhs), annotations)(statementLoc) reverse_::: acc case N => if tups.nonEmpty then raise(ErrorReport(msg"Expected a right-hand side for let bindings with parameters" -> hd.toLoc :: Nil)) - LetDecl(sym, annotations)(hd.toLoc) :: acc + LetDecl(sym, annotations)(statementLoc) :: acc (ctx + (id.name -> sym)) givenIn: - go(sts, Nil, newAcc) + go(sts, Nil, newAcc)(N) case (tree @ LetLike(Keywrd(`let`), lhs, _, N)) :: sts => raise(ErrorReport(msg"Unsupported let binding shape" -> tree.toLoc :: Nil)) - go(sts, Nil, Term.Error().withLocOf(tree) :: acc) + go(sts, Nil, Term.Error().withLocOf(tree) :: acc)(N) case (definition @ Def(lhs, rhs)) :: sts => reportUnusedAnnotations lhs match @@ -1926,23 +1930,23 @@ extends Importer: ctx.get(id.name) match case S(elem) => elem.symbol match - case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r)(definition.toLoc) :: acc) + case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r)(statementLoc) :: acc)(N) case S(sym) => raise(ErrorReport(msg"Symbol '${id.name}' is not a variable and cannot be reassigned" -> id.toLoc :: Nil)) - go(sts, Nil, Term.Error().withLocOf(id) :: acc) + go(sts, Nil, Term.Error().withLocOf(id) :: acc)(N) case N => raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) - go(sts, Nil, Term.Error().withLocOf(id) :: acc) + go(sts, Nil, Term.Error().withLocOf(id) :: acc)(N) case N => // TODO lookup in members? inherited/refined stuff? raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) - go(sts, Nil, Term.Error().withLocOf(id) :: acc) + go(sts, Nil, Term.Error().withLocOf(id) :: acc)(N) case App(base, args) => - go(Def(base, InfixApp(args, Keywrd(Keyword.`=>`), rhs)) :: sts, Nil, acc) + go(Def(base, InfixApp(args, Keywrd(Keyword.`=>`), rhs)) :: sts, Nil, acc)(statementLoc) case _ => raise(ErrorReport(msg"Unrecognized definitional assignment left-hand side: ${lhs.describe}" -> lhs.toLoc :: Nil)) // TODO BE - go(sts, Nil, Term.Error().withLocOf(lhs) :: acc) + go(sts, Nil, Term.Error().withLocOf(lhs) :: acc)(N) case (td @ TermDef(k, nme, rhs)) :: sts => log(s"Processing term definition $nme") td.symbName match @@ -1958,15 +1962,15 @@ extends Importer: if (k is MutVal) && owner.isEmpty then raise: ErrorReport: - msg"Mutable 'val' definitions are only valid as members of a module, object, or class definition" -> td.toLoc + msg"Mutable 'val' definitions are only valid as members of a module, object, or class definition" -> statementLoc :: Nil - return go(sts, Nil, acc) + return go(sts, Nil, acc)(N) if owner.isDefined && !identifierPattern.matches(id.name) then raise: ErrorReport: msg"Illegal ${k.desc} member name: '${id.name}'" -> nme.toLoc :: illegalMemberNameTail - return go(sts, Nil, acc) + return go(sts, Nil, acc)(N) val isMethod = owner.exists(_.isInstanceOf[ClassSymbol]) val tdf = ctx.nest(OuterCtx.NonReturnContext).givenIn: newCtx ?=> // * Add type parameters to context @@ -1997,7 +2001,7 @@ extends Importer: val hasGeneratorAnnotation = annotations.exists(_.isInstanceOf[Annot.Generator]) val hasAsyncAnnotation = annotations.exists(_.isInstanceOf[Annot.Async]) if pss.isEmpty && hasGeneratorAnnotation then - raise(ErrorReport(msg"Generators are not supported on functions without a parameter list" -> td.toLoc :: Nil)) + raise(ErrorReport(msg"Generators are not supported on functions without a parameter list" -> statementLoc :: Nil)) newCtx.nest(OuterCtx.Function(nonLocalRetHandler)(pss.nonEmpty && hasGeneratorAnnotation, hasAsyncAnnotation)).givenIn: newCtx ?=> val b = term(rhs)(using newCtx) if nonLocalRetHandler.directRefs.isEmpty then b else @@ -2090,16 +2094,16 @@ extends Importer: case _ => N val tsym = TermSymbol(k, owner, id, erasedType = erasedTpe) // TODO? val tdf = TermDefinition(k, sym, tsym, pss, tps, s, body, - TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N)(td.toLoc) + TermDefFlags.empty.copy(isMethod = isMethod), mfn, annotations, N)(statementLoc) tsym.defn = S(tdf) sym.tsym = S(tsym) tdf - go(sts, Nil, tdf :: acc) + go(sts, Nil, tdf :: acc)(N) case L(d) => reportUnusedAnnotations raise(d) - go(sts, Nil, acc) + go(sts, Nil, acc)(N) case (td @ TypeDef(k, head, rhs)) :: sts => val owner = ctx.outer.inner @@ -2118,14 +2122,14 @@ extends Importer: case R(id) => id case L(d) => raise(d) - return go(sts, Nil, acc) + return go(sts, Nil, acc)(N) if owner.isDefined && !identifierPattern.matches(nme.name) then raise: ErrorReport: msg"Illegal ${k.desc} member name: '${nme.name}'" -> nme.toLoc :: illegalMemberNameTail - return go(sts, Nil, acc) + return go(sts, Nil, acc)(N) val sym = members.getOrElse(nme.name, lastWords(s"Symbol not found: ${nme.name}")) @@ -2279,7 +2283,7 @@ extends Importer: assert(body.isEmpty) val d = given Ctx = newCtx - semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations)(td.toLoc) + semantics.TypeDef(alsSym, sym, tps, rhs.map(term(_)), N, annotations)(statementLoc) alsSym.defn = S(d) d case Pat => @@ -2288,7 +2292,7 @@ extends Importer: if pss.length > 1 then raise: ErrorReport: msg"Multiple parameter lists are not supported for this definition." -> - td.toLoc :: Nil + statementLoc :: Nil // Pattern definition should not have a body like class definition. assert(body.isEmpty) val ps = pss.headOption @@ -2311,7 +2315,7 @@ extends Importer: log(s"`${patSym.nme}`'s extraction parameters: ${extractionParams.mkString("[", ", ", "]")}") // Empty pattern body is considered as wildcard patterns. val rhs = td.rhs.getOrElse: - raise(ErrorReport(msg"Pattern definitions must have a body." -> td.toLoc :: Nil)) + raise(ErrorReport(msg"Pattern definitions must have a body." -> statementLoc :: Nil)) Tree.Under() // Elaborate the pattern body with the pattern parameters. val pat = pattern(rhs)(using ctx ++ patternParams.iterator.map(p => p.sym.name -> p.sym)) @@ -2334,7 +2338,7 @@ extends Importer: // `paramsOpt` is set to `N` because we don't want parameters to // appear in the generated class's constructor. val pd = PatternDef(owner, patSym, sym, tps, allParams, - patternParams, extractionParams, pat, annotations)(td.toLoc) + patternParams, extractionParams, pat, annotations)(statementLoc) patSym.defn = S(pd) pd case k: (Mod.type | Obj.type) => @@ -2350,7 +2354,7 @@ extends Importer: val md = val (bod, c) = mkBody(Nil) ModuleOrObjectDef(owner, modSym, sym, - tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx.scope, td.toLoc) + tps, pss.headOption, pss.tailOr(Nil), newOf(td), k, ObjBody(bod), comp, annotations)(outerCtx.scope, statementLoc) modSym.defn = S(md) md case Cls => @@ -2395,7 +2399,7 @@ extends Importer: case a @ Annot.Modifier(Keyword.`declare`) => a , S(clsSym), - )(td.toLoc) + )(statementLoc) if pss.nonEmpty then sym.tsym = S(ctsym) ctsym.defn = S(ctdef) // Note: do NOT set sym.tsym for constructor(...) classes; they are not callable as functions. @@ -2403,46 +2407,46 @@ extends Importer: else N val cd = val (bod, c) = mkBody(auxCtorPss) - ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp, auxCtorParams = auxCtorPss)(td.toLoc) + ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp, auxCtorParams = auxCtorPss)(statementLoc) clsSym.defn = S(cd) cd case Trt | Mxn => lastWords(s"Unexpected type definition kind here: $k") - go(sts, Nil, defn :: acc) + go(sts, Nil, defn :: acc)(N) case Annotated(annotation, target) :: sts => - go(target :: sts, annotations ++ annot(annotation), acc) + go(target :: sts, annotations ++ annot(annotation), acc)(statementLoc) // * With tight right precedence, `#config(args)` is parsed as `App(Directive(config, Tup()), Tup(args))`. // * Reconstruct as `Directive(config, Tup(args))` and re-process. case App(Directive(prefix, _), args) :: sts => - go(Directive(prefix, args) :: sts, annotations, acc) + go(Directive(prefix, args) :: sts, annotations, acc)(statementLoc) case (directive @ Directive(Ident("config"), Tup(args))) :: sts => reportUnusedAnnotations val modify = ConfigParser.parseOverrides(args) - go(sts, Nil, SetConfig(modify)(directive.toLoc) :: acc) + go(sts, Nil, SetConfig(modify)(statementLoc) :: acc)(N) case (directive @ Directive(Ident("lang"), Tup(args))) :: sts => reportUnusedAnnotations val modify = ConfigParser.parseLanguageDirective(args) - go(sts, Nil, SetConfig(modify)(directive.toLoc) :: acc) + go(sts, Nil, SetConfig(modify)(statementLoc) :: acc)(N) case Directive(Ident(name), _) :: sts => raise(ErrorReport( msg"Unknown directive '#${name}'" -> sts.headOption.flatMap(_.toLoc) :: Nil, source = Diagnostic.Source.Compilation)) - go(sts, annotations, acc) + go(sts, annotations, acc)(N) case (dir @ Directive(prefix, _)) :: sts => raise(ErrorReport( msg"Expected a directive name after '#', but found ${prefix.describe}" -> prefix.toLoc :: Nil, source = Diagnostic.Source.Compilation)) - go(sts, annotations, acc) + go(sts, annotations, acc)(N) case (st: Tree) :: sts => // TODO reject plain term statements? Currently, `(1, 2)` is allowed to elaborate (tho it should be rejected in type checking later) val res = annotations.foldLeft(term(st)): case (acc, ann) => Term.Annotated(ann, acc) sts match case Nil => (mkBlk(acc, S(res), hasResult), ctx) - case _ => go(sts, Nil, res :: acc) + case _ => go(sts, Nil, res :: acc)(N) end go ctx.withMembers(members).givenIn: - go(blk.desugStmts, Nil, Nil) + go(blk.desugStmts, Nil, Nil)(N) def mkBlk(acc: Ls[Statement], res: Opt[Term], hasResult: Bool): Blk | Rcd = diff --git a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls index 7b6e979b76..f73bb10db5 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls @@ -48,6 +48,8 @@ class A2(x) with @buffered class B0(x) //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. +//│ ║ l.48: @buffered +//│ ║ ^^^^^^^^^ //│ ║ l.49: class B0(x) //│ ╙── ^^^^^^^^^^^ @@ -56,12 +58,16 @@ class B0(x) @buffered class B1(x) //│ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. -//│ ║ l.55: @buffered +//│ ║ l.57: @buffered //│ ║ ^^^^^^^^^ -//│ ║ l.56: @buffered +//│ ║ l.58: @buffered //│ ╙── ^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//│ ║ l.57: class B1(x) +//│ ║ l.57: @buffered +//│ ║ ^^^^^^^^^ +//│ ║ l.58: @buffered +//│ ║ ^^^^^^^^^ +//│ ║ l.59: class B1(x) //│ ╙── ^^^^^^^^^^^ :ge @@ -69,26 +75,30 @@ class B1(x) @bufferable class B2(x) //│ ╔══[COMPILATION ERROR] Only one of bufferable annotation is allowed. -//│ ║ l.68: @buffered +//│ ║ l.74: @buffered //│ ║ ^^^^^^^^^ -//│ ║ l.69: @bufferable +//│ ║ l.75: @bufferable //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. -//│ ║ l.70: class B2(x) +//│ ║ l.74: @buffered +//│ ║ ^^^^^^^^^ +//│ ║ l.75: @bufferable +//│ ║ ^^^^^^^^^^^ +//│ ║ l.76: class B2(x) //│ ╙── ^^^^^^^^^^^ :w @bufferable module B3 //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.81: @bufferable +//│ ║ l.91: @bufferable //│ ╙── ^^^^^^^^^^^ :w @bufferable object B3 //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.88: @bufferable +//│ ║ l.98: @bufferable //│ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls index e45fe7be13..c0dccc581e 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls @@ -1,15 +1,50 @@ +:. :js data class Pair[A, B](val first: A, val second: B) :e pattern LessArity = Pair("meow") -//│ ╔══[COMPILATION ERROR] Expected two arguments, but found only one argument. -//│ ║ l.6: pattern LessArity = Pair("meow") -//│ ╙── ^^^^^^ +//│ ╔══[COMPILATION ERROR] Resolution error in constructor +//│ ║ l.7: pattern LessArity = Pair("meow") +//│ ║ ^^^^^^^^^^^^ +//│ ╟── Class definition 'Pair' expected 2 pattern arguments, but got 1 +//│ ║ l.4: data class Pair[A, B](val first: A, val second: B) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e pattern MoreArity = Pair("meow", "woof", "moo") -//│ ╔══[COMPILATION ERROR] Expected two arguments, but found three arguments. -//│ ║ l.12: pattern MoreArity = Pair("meow", "woof", "moo") -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[COMPILATION ERROR] Resolution error in constructor +//│ ║ l.16: pattern MoreArity = Pair("meow", "woof", "moo") +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Class definition 'Pair' expected 2 pattern arguments, but got 3 +//│ ║ l.4: data class Pair[A, B](val first: A, val second: B) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Nested modifiers and annotations belong to the whole declaration span. +open annotations +@untyped +public data class AnnotatedPair(val first, val second) + +:e +pattern WrongAnnotatedArity = AnnotatedPair("one") +//│ ╔══[COMPILATION ERROR] Resolution error in constructor +//│ ║ l.30: pattern WrongAnnotatedArity = AnnotatedPair("one") +//│ ║ ^^^^^^^^^^^^^^^^^^^^ +//│ ╟── Class definition 'AnnotatedPair' expected 2 pattern arguments, but got 1 +//│ ║ l.26: @untyped +//│ ║ ^^^^^^^^ +//│ ║ l.27: public data class AnnotatedPair(val first, val second) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// A following declaration must not inherit the preceding annotation's span. +data class NextPair(val first, val second) + +:e +pattern WrongNextArity = NextPair("one") +//│ ╔══[COMPILATION ERROR] Resolution error in constructor +//│ ║ l.44: pattern WrongNextArity = NextPair("one") +//│ ║ ^^^^^^^^^^^^^^^ +//│ ╟── Class definition 'NextPair' expected 2 pattern arguments, but got 1 +//│ ║ l.41: data class NextPair(val first, val second) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 6f1d92be02d083e646945e6e6ff714d8e2fd933b Mon Sep 17 00:00:00 2001 From: Codex Date: Tue, 29 Sep 2026 22:44:53 +0800 Subject: [PATCH 4/5] Adapt location regression tests and goldens to hkmc2 --- .../src/test/mlscript/backlog/ToTriage.mls | 2 +- .../src/test/mlscript/basics/DataClass.mls | 2 + .../mlscript/codegen/BadConfigDirective.mls | 8 ++-- .../test/mlscript/codegen/BadGenerators.mls | 16 +++++-- .../test/mlscript/codegen/SourceLocations.mls | 11 ++--- .../src/test/mlscript/codegen/Spreads.mls | 4 +- .../src/test/mlscript/objbuf/BasicsObjBuf.mls | 23 +++++++++ .../ups/fixpoint/UnsupportedShapes.mls | 2 +- .../ups/parametric/HigherOrderPattern.mls | 4 +- .../test/mlscript/ups/parametric/Nullable.mls | 2 +- .../mlscript/ups/syntax/WrongArguments.mls | 4 +- .../test/mlscript/ups/syntax/WrongArity.mls | 47 +++---------------- .../test/mlscript/wasm/BuiltinOperators.mls | 2 +- .../src/test/mlscript/wasm/ClassMethods.mls | 2 +- 14 files changed, 63 insertions(+), 66 deletions(-) diff --git a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls index fe15640596..4241c0e83c 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/ToTriage.mls @@ -238,7 +238,7 @@ id(Foo)(1) data class FooSpd(...args) //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.238: data class FooSpd(...args) -//│ ╙── ^^^^ +//│ ╙── ^^^^^^^ FooSpd(1, 2, 3).args //│ ╔══[COMPILATION ERROR] Expected 0 arguments, got 3 diff --git a/hkmc2/shared/src/test/mlscript/basics/DataClass.mls b/hkmc2/shared/src/test/mlscript/basics/DataClass.mls index 5ec090d93b..85084ad21c 100644 --- a/hkmc2/shared/src/test/mlscript/basics/DataClass.mls +++ b/hkmc2/shared/src/test/mlscript/basics/DataClass.mls @@ -11,6 +11,8 @@ Foo(1).x :todo data Foo(x: Int) //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.12: data Foo(x: Int) +//│ ║ ^^^^ //│ ╟── This annotation is not supported on application of type Foo terms. //│ ║ l.12: data Foo(x: Int) //│ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls b/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls index 1cff34535c..f3107be307 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadConfigDirective.mls @@ -4,6 +4,8 @@ :w @config(tailRecOpt: false) "hi" //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.5: @config(tailRecOpt: false) "hi" +//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╟── This annotation is not supported on string literal terms. //│ ║ l.5: @config(tailRecOpt: false) "hi" //│ ╙── ^^^^ @@ -18,7 +20,7 @@ :e #lang(1.0.x) //│ ╔══[COMPILATION ERROR] Unknown language version '1.0.x' -//│ ║ l.19: #lang(1.0.x) +//│ ║ l.21: #lang(1.0.x) //│ ║ ^^^^^ //│ ╙── Available language versions: 0.2.x, 0.3.x @@ -26,12 +28,12 @@ :e #lang(unknownLanguageField: true) //│ ╔══[COMPILATION ERROR] Unsupported Language argument -//│ ║ l.27: #lang(unknownLanguageField: true) +//│ ║ l.29: #lang(unknownLanguageField: true) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ :e #lang(versionName: "custom") //│ ╔══[COMPILATION ERROR] Unsupported Language argument -//│ ║ l.34: #lang(versionName: "custom") +//│ ║ l.36: #lang(versionName: "custom") //│ ╙── ^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls b/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls index 7c59fd113a..0f34cf4694 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BadGenerators.mls @@ -13,6 +13,8 @@ yield 1 @generator () => 3 //│ ╔══[WARNING] This annotation has no effect. +//│ ║ l.13: @generator +//│ ║ ^^^^^^^^^^ //│ ╟── This annotation is not supported on function literal terms. //│ ║ l.14: () => 3 //│ ╙── ^^^^^^^ @@ -25,17 +27,21 @@ class C with fun f = yield 1 //│ ╔══[COMPILATION ERROR] Generators are not supported on functions without a parameter list -//│ ║ l.25: fun f = -//│ ║ ^^^^^^^ -//│ ║ l.26: yield 1 +//│ ║ l.26: @generator +//│ ║ ^^^^^^^^^^ +//│ ║ l.27: fun f = +//│ ║ ^^^^^^^^^ +//│ ║ l.28: yield 1 //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Yield expressions are not allowed in this context. -//│ ║ l.26: yield 1 +//│ ║ l.28: yield 1 //│ ╙── ^^^^^^^ :ge @generator fun bad = 1 //│ ╔══[COMPILATION ERROR] Generators are not supported on functions without a parameter list -//│ ║ l.38: fun bad = 1 +//│ ║ l.41: @generator +//│ ║ ^^^^^^^^^^ +//│ ║ l.42: fun bad = 1 //│ ╙── ^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls index 82775ba8e7..0ff4d0f70e 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/SourceLocations.mls @@ -1,4 +1,3 @@ -:. :js open annotations @@ -9,10 +8,10 @@ class First @inline class Second //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.7: @inline +//│ ║ l.6: @inline //│ ╙── ^^^^^^^ //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.9: @inline +//│ ║ l.8: @inline //│ ╙── ^^^^^^^ // Annotations on let bindings point to the annotation occurrence. @@ -20,7 +19,7 @@ class Second @tailrec let value = 1 //│ ╔══[WARNING] This annotation has no effect. -//│ ║ l.20: @tailrec +//│ ║ l.19: @tailrec //│ ╙── ^^^^^^^^ //│ value = 1 @@ -29,7 +28,7 @@ let value = 1 @inline fun abstractFunction() //│ ╔══[WARNING] Only functions with a body may be marked as @inline. -//│ ║ l.29: @inline +//│ ║ l.28: @inline //│ ╙── ^^^^^^^ // This error is emitted from lowered IR after the prefix has become a temporary. @@ -38,6 +37,6 @@ fun abstractFunction() let index = 0 [(x => x)].(index)(1) //│ ╔══[COMPILATION ERROR] Cannot determine if the dynamic selection is a function object. -//│ ║ l.39: [(x => x)].(index)(1) +//│ ║ l.38: [(x => x)].(index)(1) //│ ╙── ^^^^^^^^^^^^^^^^^ //│ index = 0 diff --git a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls index 9546efd078..1297e4b80a 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/Spreads.mls @@ -60,7 +60,7 @@ let x = new A(1, 2, 3) x.r //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.58: data class A(...r) -//│ ╙── ^ +//│ ╙── ^^^^ //│ ═══[RUNTIME ERROR] Error: Access to required field 'r' yielded 'undefined' //│ ═══[RUNTIME ERROR] Expected: '[1, 2, 3]', got: 'undefined' //│ x = A() @@ -72,7 +72,7 @@ data class A(...r) with new A(1, 2, 3).getR //│ ╔══[COMPILATION ERROR] Spread parameters are not supported in class parameters. //│ ║ l.70: data class A(...r) with -//│ ╙── ^ +//│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] No definition found in scope for member 'r' //│ ║ l.71: fun getR = r //│ ║ ^ diff --git a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls index f73bb10db5..4dc81a4357 100644 --- a/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls +++ b/hkmc2/shared/src/test/mlscript/objbuf/BasicsObjBuf.mls @@ -170,3 +170,26 @@ class A with fun f(y) = x + y + z + +// The declaration span includes nested modifiers and multiline annotations. +:ge +@buffered +@untyped +public data class LocatedBuffer(x) +//│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. +//│ ║ l.176: @buffered +//│ ║ ^^^^^^^^^ +//│ ║ l.177: @untyped +//│ ║ ^^^^^^^^ +//│ ║ l.178: public data class LocatedBuffer(x) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// The following declaration starts a fresh span. +:ge +@buffered +class NextLocatedBuffer(x) +//│ ╔══[COMPILATION ERROR] Buffered classes must not have a main parameter list; use `constructor(...)` syntax instead. +//│ ║ l.189: @buffered +//│ ║ ^^^^^^^^^ +//│ ║ l.190: class NextLocatedBuffer(x) +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls index ae1a3de6c1..96aa843a09 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/UnsupportedShapes.mls @@ -247,7 +247,7 @@ pattern ArityGo = ArityCtx(AritySum) as ArityGo | _ fun arityGo(x) = if x is @compile ArityGo(r) then r else "" //│ ╔══[COMPILATION ERROR] Class `Pair` has 2 parameters. //│ ║ l.13: data class Pair(val first, val second) -//│ ║ ^^^^^^^^^^^^^^^^^ +//│ ║ ^^^^^^^^^^^^^^^^^^^^^ //│ ╟── But 1 arguments were provided. //│ ║ l.245: pattern ArityCtx(pattern H) = H | Pair(ArityCtx(H)) //│ ╙── ^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls index 8078124cca..27d8f6eb6c 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/HigherOrderPattern.mls @@ -10,7 +10,7 @@ object A null is @compile Nullable //│ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. //│ ║ l.5: pattern Nullable(pattern T) = null | T -//│ ║ ^ +//│ ║ ^^^^^^^^^ //│ ╟── But zero pattern arguments were provided. //│ ║ l.10: null is @compile Nullable //│ ╙── ^^^^^^^^ @@ -20,7 +20,7 @@ null is @compile Nullable null is @compile Nullable(A, A) //│ ╔══[COMPILATION ERROR] Pattern `Nullable` has one pattern parameter. //│ ║ l.5: pattern Nullable(pattern T) = null | T -//│ ║ ^ +//│ ║ ^^^^^^^^^ //│ ╟── But two pattern arguments were provided. //│ ║ l.20: null is @compile Nullable(A, A) //│ ╙── ^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls b/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls index 0816291424..23aec5fb13 100644 --- a/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls +++ b/hkmc2/shared/src/test/mlscript/ups/parametric/Nullable.mls @@ -60,7 +60,7 @@ null is Optional(Int) as Some(42) fun toIntOption(x) = if x is (@compile Optional) as o then o //│ ╔══[COMPILATION ERROR] Pattern `Optional` has one pattern parameter. //│ ║ l.47: pattern Optional(pattern T) = (null => None) | ((T as x) => Some(x)) -//│ ║ ^ +//│ ║ ^^^^^^^^^ //│ ╟── But zero pattern arguments were provided. //│ ║ l.60: fun toIntOption(x) = if x is (@compile Optional) as o then o //│ ╙── ^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls index 4f3f6a5364..84d392023d 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArguments.mls @@ -16,7 +16,7 @@ pattern Baz(pattern A) = A pattern Baz(pattern A) = A() //│ ╔══[COMPILATION ERROR] `A` is a pattern parameter. //│ ║ l.16: pattern Baz(pattern A) = A() -//│ ║ ^ +//│ ║ ^^^^^^^^^ //│ ╟── It cannot be applied. //│ ║ l.16: pattern Baz(pattern A) = A() //│ ╙── ^ @@ -25,7 +25,7 @@ pattern Baz(pattern A) = A() pattern Baz(pattern A) = A(1, 2, 3) //│ ╔══[COMPILATION ERROR] `A` is a pattern parameter. //│ ║ l.25: pattern Baz(pattern A) = A(1, 2, 3) -//│ ║ ^ +//│ ║ ^^^^^^^^^ //│ ╟── It cannot be applied to any arguments. //│ ║ l.25: pattern Baz(pattern A) = A(1, 2, 3) //│ ╙── ^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls index c0dccc581e..e45fe7be13 100644 --- a/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls +++ b/hkmc2/shared/src/test/mlscript/ups/syntax/WrongArity.mls @@ -1,50 +1,15 @@ -:. :js data class Pair[A, B](val first: A, val second: B) :e pattern LessArity = Pair("meow") -//│ ╔══[COMPILATION ERROR] Resolution error in constructor -//│ ║ l.7: pattern LessArity = Pair("meow") -//│ ║ ^^^^^^^^^^^^ -//│ ╟── Class definition 'Pair' expected 2 pattern arguments, but got 1 -//│ ║ l.4: data class Pair[A, B](val first: A, val second: B) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[COMPILATION ERROR] Expected two arguments, but found only one argument. +//│ ║ l.6: pattern LessArity = Pair("meow") +//│ ╙── ^^^^^^ :e pattern MoreArity = Pair("meow", "woof", "moo") -//│ ╔══[COMPILATION ERROR] Resolution error in constructor -//│ ║ l.16: pattern MoreArity = Pair("meow", "woof", "moo") -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ╟── Class definition 'Pair' expected 2 pattern arguments, but got 3 -//│ ║ l.4: data class Pair[A, B](val first: A, val second: B) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -// Nested modifiers and annotations belong to the whole declaration span. -open annotations -@untyped -public data class AnnotatedPair(val first, val second) - -:e -pattern WrongAnnotatedArity = AnnotatedPair("one") -//│ ╔══[COMPILATION ERROR] Resolution error in constructor -//│ ║ l.30: pattern WrongAnnotatedArity = AnnotatedPair("one") -//│ ║ ^^^^^^^^^^^^^^^^^^^^ -//│ ╟── Class definition 'AnnotatedPair' expected 2 pattern arguments, but got 1 -//│ ║ l.26: @untyped -//│ ║ ^^^^^^^^ -//│ ║ l.27: public data class AnnotatedPair(val first, val second) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -// A following declaration must not inherit the preceding annotation's span. -data class NextPair(val first, val second) - -:e -pattern WrongNextArity = NextPair("one") -//│ ╔══[COMPILATION ERROR] Resolution error in constructor -//│ ║ l.44: pattern WrongNextArity = NextPair("one") -//│ ║ ^^^^^^^^^^^^^^^ -//│ ╟── Class definition 'NextPair' expected 2 pattern arguments, but got 1 -//│ ║ l.41: data class NextPair(val first, val second) -//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +//│ ╔══[COMPILATION ERROR] Expected two arguments, but found three arguments. +//│ ║ l.12: pattern MoreArity = Pair("meow", "woof", "moo") +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls index fd4e9f3807..b956bb7886 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/BuiltinOperators.mls @@ -82,7 +82,7 @@ fun constOf(x: Int) = wasm.ref.i31(wasm.i32.const(x)) :ge wasm.i32.const(-1) //│ ╔══[COMPILATION ERROR] Wasm intrinsic 'i32.const' expects an integer literal immediate -//│ ║ l.88: wasm.i32.const(-1) +//│ ║ l.83: wasm.i32.const(-1) //│ ╙── ^^ diff --git a/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls b/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls index 94b2c3deaf..fc1ccad316 100644 --- a/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls +++ b/hkmc2/shared/src/test/mlscript/wasm/ClassMethods.mls @@ -203,7 +203,7 @@ class A(val x) with A(1).get //│ ╔══[COMPILATION ERROR] `member:get` is neither a field access nor a callable method //│ ║ l.203: A(1).get -//│ ╙── ^^^^ +//│ ╙── ^^^^^^^^ // Method result types are taken from the return annotation, so primitives stay unboxed From 0499ef3dc6867571bedb0522b3a42b432ea55906 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 30 Sep 2026 14:10:39 +0800 Subject: [PATCH 5/5] Construct folded literals with the original operation location --- .../src/main/scala/hkmc2/codegen/Block.scala | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 65c9376572..c8de517df4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -1200,9 +1200,9 @@ object Call: case Value.SimpleRef(sym: BuiltinSymbol) => argss match case (Arg(N, arg1: Value) :: Arg(N, arg2: Value) :: Nil) :: Nil => - evalBuiltin(sym, arg1, arg2)(value => return value.withLoc(toLoc)) + evalBuiltin(sym, arg1, arg2)(toLoc)(return _) case (Arg(N, arg1: Value) :: Nil) :: Nil => - evalBuiltin(sym, arg1)(value => return value.withLoc(toLoc)) + evalBuiltin(sym, arg1)(toLoc)(return _) case _ => case _ => raw(fun, argss)(metadata, toLoc) @@ -1214,29 +1214,29 @@ object Call: import Value.Lit - private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value, arg2: Value)(inline k: Value => Unit): Unit = + private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value, arg2: Value)(toLoc: Opt[Loc])(inline k: Value => Unit): Unit = (sym.nme, arg1, arg2) match - case ("+", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 + v2))(N)) - case ("+", Lit(Tree.StrLit(v1)), Lit(Tree.StrLit(v2))) => k(Lit(Tree.StrLit(v1 + v2))(N)) - case ("-", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 - v2))(N)) - case ("*", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 * v2))(N)) + case ("+", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 + v2))(toLoc)) + case ("+", Lit(Tree.StrLit(v1)), Lit(Tree.StrLit(v2))) => k(Lit(Tree.StrLit(v1 + v2))(toLoc)) + case ("-", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 - v2))(toLoc)) + case ("*", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.IntLit(v1 * v2))(toLoc)) // * For "/", should check for 0 and return a DecLit. - case ("%", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) if v2 =/= 0 => k(Lit(Tree.IntLit(v1 % v2))(N)) - case ("===", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 == l2))(N)) - case ("!==", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 != l2))(N)) - case ("<", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 < v2))(N)) - case ("<=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 <= v2))(N)) - case (">", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 > v2))(N)) - case (">=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 >= v2))(N)) - case ("&&", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 && v2))(N)) - case ("||", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 || v2))(N)) + case ("%", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) if v2 =/= 0 => k(Lit(Tree.IntLit(v1 % v2))(toLoc)) + case ("===", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 == l2))(toLoc)) + case ("!==", Lit(l1), Lit(l2)) => k(Lit(Tree.BoolLit(l1 != l2))(toLoc)) + case ("<", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 < v2))(toLoc)) + case ("<=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 <= v2))(toLoc)) + case (">", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 > v2))(toLoc)) + case (">=", Lit(Tree.IntLit(v1)), Lit(Tree.IntLit(v2))) => k(Lit(Tree.BoolLit(v1 >= v2))(toLoc)) + case ("&&", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 && v2))(toLoc)) + case ("||", Lit(Tree.BoolLit(v1)), Lit(Tree.BoolLit(v2))) => k(Lit(Tree.BoolLit(v1 || v2))(toLoc)) case _ => - private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value)(inline k: Value => Unit): Unit = + private inline def evalBuiltin(sym: BuiltinSymbol, arg1: Value)(toLoc: Opt[Loc])(inline k: Value => Unit): Unit = (sym.nme, arg1) match - case ("+", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(v1))(N)) - case ("-", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(-v1))(N)) - case ("!", Lit(Tree.BoolLit(v))) => k(Lit(Tree.BoolLit(!v))(N)) + case ("+", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(v1))(toLoc)) + case ("-", Lit(Tree.IntLit(v1))) => k(Lit(Tree.IntLit(-v1))(toLoc)) + case ("!", Lit(Tree.BoolLit(v))) => k(Lit(Tree.BoolLit(!v))(toLoc)) case _ => end Call