From f4ecb0f1eeba11b120398ad3418ffe723d12eac5 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 11:32:14 +0800 Subject: [PATCH 01/13] misc: Fix non-local return warnings --- .../scala/hkmc2/invalml/TypeSimplifier.scala | 5 +- .../main/scala/hkmc2/semantics/Resolver.scala | 129 +++++++++--------- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/invalml/TypeSimplifier.scala b/hkmc2/shared/src/main/scala/hkmc2/invalml/TypeSimplifier.scala index 405a02e79e..c1f7b31ce6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/invalml/TypeSimplifier.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/invalml/TypeSimplifier.scala @@ -2,6 +2,7 @@ package hkmc2.invalml import scala.collection.mutable.{Map => MutMap, Set => MutSet, LinkedHashMap, LinkedHashSet} import scala.collection.immutable.{SortedMap, SortedSet} +import scala.util.boundary, boundary.break import scala.util.chaining._ import mlscript.utils.*, shorthands.* @@ -73,14 +74,14 @@ class TypeSimplifier(tl: TraceLogger): case N => tv } - override def apply(pol: Bool)(ty: GeneralType): Unit = + override def apply(pol: Bool)(ty: GeneralType): Unit = boundary: trace(s"Analyse[${printPol(pol)}] ${ty.showDbg} [${curPath.reverseIterator.mkString(" ~> ")}]"): ty match case ty if ty.lvl <= lvl => log(s"Level is < $lvl") case tv: IV if { occsNum(tv) = occsNum.getOrElse(tv, 0) + 1; false } => case tv: IV => - if varSubst.contains(tv) then return log(s"Already subst'd") // * If the IV was set to be substituted, it means it's been found recursive and we don't need to traverse it again + if varSubst.contains(tv) then break(log(s"Already subst'd")) // * If the IV was set to be substituted, it means it's been found recursive and we don't need to traverse it again var continue = true // if (!traversedTVs.contains(tv)) { if curPath.exists(_ is tv) then // TODO opt diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala index 3a725109a7..3c6d4232d4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala @@ -16,6 +16,7 @@ import semantics.ucs.FlatPattern import Message.MessageContext import scala.annotation.tailrec +import scala.util.boundary, boundary.break object Resolver: @@ -1035,70 +1036,70 @@ class Resolver(tl: TraceLogger) * @param expect the expectation on the type. See [[Expect]] for * details. */ - def traverseSign(t: Term, expect: Expect, inAppPrefix: Bool = false)(using ictx: ICtx): Unit = - trace(s"Traversing type ${t}, expecting ${expect}"): - - // * Traverse through the sub-terms. - t match - case Term.Ref(_) => - case Term.Lit(_) => - case Term.Tup(_) => t.subTerms.foreach(traverse(_, expect = NonModule(N))) - case Term.UnitVal() => - // Literals with operators. e.g., -42 - case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => - - // Selection. The prefix should be a term, rather than a type, that - // can be selected from. This should not be a selection projection. - case AnySel(base, _, N) => - base.subTerms.foreach(traverse(_, expect = Any)) - - // Type Application. Traverse the type constructor and arguments, - // respectively. - case Term.TyApp(con, targs) => - traverseSign(con, expect = expect, inAppPrefix = true) - targs.foreach(traverseSign(_, expect = Expect.NonModule(S("Type arguments should be non-moduleful types.")))) - - // Complex type: Function type, Wildcard type, Composed type, - // Negation type, Forall type, - case t: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup) => - t.subTerms.foreach(traverseSign(_, expect = Expect.NonModule(N))) - - // t is not a type. - case _ => - raise(ErrorReport(msg"Expected a type, got ${t.describe}" -> t.toLoc :: Nil)) - return - - - - // * Resolve the symbol and type of the term. - val typ = t match - case t: Resolvable => - resolveSymbol(t, prefer = expect, sign = true) - val typ = resolveSign(t, expect = expect) - t.expandedResolvableIn(_.withTyp(typ)) - typ - case _ => - val typ = resolveSign(t, expect = expect) - typ - - // * Check if the term satisfies the expectation. - // * Check the arity of type params/args. - typ match - case Type.Ref(sym: TypeSymbol, targs) if !inAppPrefix => - sym.defn.flatMap(CallableDefinition.fromDefn(_)).foreach: - case CallableDefinition(tparams = tparams) => - val tparamsNum = tparams.map(_.length) - val targsNum = targs.length - if tparamsNum.getOrElse(0) =/= targsNum then - val tparamsMsg = tparamsNum.getOrElse("no").toString - val targsMsg = if targsNum === 0 then "none" else targsNum.toString - raise: - ErrorReport: - msg"Expected ${tparamsMsg} type arguments, " - + msg"got ${targsMsg}" -> t.toLoc :: Nil - case Type.Ref(sym: VarSymbol, targs) if !inAppPrefix => - // TODO: check arity? - case _ => + def traverseSign(t: Term, expect: Expect, inAppPrefix: Bool = false)(using ictx: ICtx): Unit = boundary: + trace(s"Traversing type ${t}, expecting ${expect}"): + + // * Traverse through the sub-terms. + t match + case Term.Ref(_) => + case Term.Lit(_) => + case Term.Tup(_) => t.subTerms.foreach(traverse(_, expect = NonModule(N))) + case Term.UnitVal() => + // Literals with operators. e.g., -42 + case Term.App(Term.Ref(_: BuiltinSymbol), Term.Tup(Fld(term = Term.Lit(_)) :: Nil)) => + + // Selection. The prefix should be a term, rather than a type, that + // can be selected from. This should not be a selection projection. + case AnySel(base, _, N) => + base.subTerms.foreach(traverse(_, expect = Any)) + + // Type Application. Traverse the type constructor and arguments, + // respectively. + case Term.TyApp(con, targs) => + traverseSign(con, expect = expect, inAppPrefix = true) + targs.foreach(traverseSign(_, expect = Expect.NonModule(S("Type arguments should be non-moduleful types.")))) + + // Complex type: Function type, Wildcard type, Composed type, + // Negation type, Forall type, + case t: (Term.FunTy | Term.WildcardTy | Term.CompType | Term.Neg | Term.Forall | Term.Constrained | Term.Tup) => + t.subTerms.foreach(traverseSign(_, expect = Expect.NonModule(N))) + + // t is not a type. + case _ => + raise(ErrorReport(msg"Expected a type, got ${t.describe}" -> t.toLoc :: Nil)) + break() + + + + // * Resolve the symbol and type of the term. + val typ = t match + case t: Resolvable => + resolveSymbol(t, prefer = expect, sign = true) + val typ = resolveSign(t, expect = expect) + t.expandedResolvableIn(_.withTyp(typ)) + typ + case _ => + val typ = resolveSign(t, expect = expect) + typ + + // * Check if the term satisfies the expectation. + // * Check the arity of type params/args. + typ match + case Type.Ref(sym: TypeSymbol, targs) if !inAppPrefix => + sym.defn.flatMap(CallableDefinition.fromDefn(_)).foreach: + case CallableDefinition(tparams = tparams) => + val tparamsNum = tparams.map(_.length) + val targsNum = targs.length + if tparamsNum.getOrElse(0) =/= targsNum then + val tparamsMsg = tparamsNum.getOrElse("no").toString + val targsMsg = if targsNum === 0 then "none" else targsNum.toString + raise: + ErrorReport: + msg"Expected ${tparamsMsg} type arguments, " + + msg"got ${targsMsg}" -> t.toLoc :: Nil + case Type.Ref(sym: VarSymbol, targs) if !inAppPrefix => + // TODO: check arity? + case _ => /** * Given a symbol-resolved term that represents a type, resolve the From b2d516696cacc1fcc442c57c22e4deb736872a0f Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 11:36:18 +0800 Subject: [PATCH 02/13] misc: Remove dead match branches --- .../src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala | 1 - .../src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index f1316f6d72..5528e4a1d6 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -998,7 +998,6 @@ class FlowConstraintsCollector( rest.foreach: nextArgs => nextArgs.foreach(a => cc.constrain(processResult(a.value), UnknownCons)) UnknownProd - case Nil => handleCallLike(c.uid, fun, Nil) case i@Instantiate(_, cls, argss) => handleCallLike(i.uid, cls, argss.flatten) case lam@Lambda(ps, body) => mkFunProdStrat("lam_res", ps :: Nil, body, lam.uid) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index 36a178aba9..a765f85fce 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -425,7 +425,6 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): sel.trm.resolvedTargets ::= SelectionTarget.ObjectMember(memb) log(s"Found immediate member ${memb}") toSolve.push(Constraint(getFlowSymOrType(memb), sel.res)) - case S(memb) => TODO(memb) case N => d.moduleCompanion match case S(comp) => From 6d74c5b23d651d3a0f95a4aa57f4ee09de809a45 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 11:41:21 +0800 Subject: [PATCH 03/13] misc: Remove unreachable cases that only matches with `null` --- hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala | 1 - .../src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 7b80670ce8..5d934f3ac1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -74,7 +74,6 @@ class Printer(using Raise, ShowCfg, State, SymbolPrinter, Config): case End(msg) if msg.nonEmpty && config.commentGeneratedCode => doc"end /* ${msg} */" case End(_) => doc"end" case Unreachable(msg) => doc"unreachable /* ${msg} */" - case _ => TODO(blk) def printFlags(defn: Defn)(using Scope): Document = // val overrides = defn match diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index a765f85fce..a4902a4180 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -439,9 +439,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): patho match case S(path) => sel.trm.resolvedTargets ::= SelectionTarget.CompanionMember(path, memb) - val newlhs = memb match - case memb: BlockMemberSymbol => getFlowSymOrType(memb) - case _ => TODO(memb) + val newlhs = getFlowSymOrType(memb) toSolve.push(Constraint(newlhs, C.Fun(P.Tup((N, lhs) :: Nil), sel.res))) case N => raise: sel.trm.isErroneous = true From 90d09f5c542cd91b758aba8e906e9207cfb28751 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 11:52:30 +0800 Subject: [PATCH 04/13] misc: Add `@unchecked` to provably correct erased type args --- .../src/main/scala/hkmc2/codegen/DeadParamElim.scala | 2 +- .../src/main/scala/hkmc2/codegen/EtaExpansion.scala | 8 ++++---- .../src/main/scala/hkmc2/codegen/deforest/Rewrite.scala | 8 ++++---- .../scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala | 6 +++--- .../src/main/scala/hkmc2/semantics/ups/Pattern.scala | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala index 2b75982a87..6ebcb421da 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala @@ -119,7 +119,7 @@ class DeadParamElimSolver(val constraintSolver: FlowConstraintSolver): def showProdFun(prodFun: ProdFun): Str = def showFunId(funId: FunId): Str = funId match case (funSym: Symbol, whichParamList) => s"${funSym.nme}#$whichParamList" - case exprId: ResultId => exprId.getResult match + case exprId: ResultId @unchecked => exprId.getResult match case Lambda(_, _) => s"lambda@$exprId" case _ => showRefSite(exprId) val inst = prodFun.instantiationId.fold("")(instId => s" @ ${showInstId(instId)}") diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala index 7bcca2730a..39c6e9952d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala @@ -38,7 +38,7 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): def prodFunIsAffine(pf: ProdFun) = pf.exprId match - case lamId: ResultId => + case lamId: ResultId @unchecked => lamId.getResult match case lamDef: Lambda => lamDef.affine case other => lastWords(s"expected lambda result, got $other") @@ -99,14 +99,14 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): for pf <- constraintSolver.funDests.keysIterator do pf.exprId match - case lamId: ResultId => funShape.getOrElseUpdate(lamId, etaExpansionTargetShapes(pf)(using Set.empty)) + case lamId: ResultId @unchecked => funShape.getOrElseUpdate(lamId, etaExpansionTargetShapes(pf)(using Set.empty)) case (funSym: TermSymbol, 0) => funShape.getOrElseUpdate(funSym, etaExpansionTargetShapes(pf)(using Set.empty)) case _ => () private def showFunShapeId(id: TermSymbol | ResultId): Str = id match case funSym: TermSymbol => funSym.nme - case lamId: ResultId => + case lamId: ResultId @unchecked => lamId.getResult match case Lambda(_, _) => s"lambda@$lamId" case r => lastWords(s"not lambda $r") @@ -119,7 +119,7 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): val prev = constraintSolver.preAnalyzer.res.funSymToFunDefn(funSym).params.size val now = shape.size now > prev - case lamId: ResultId => shape.size > 1 + case lamId: ResultId @unchecked => shape.size > 1 def hasEtaExpansionTargets: Bool = funShape.exists: 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 95a8d49cd6..36a9eab2b2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -39,7 +39,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): extension (matchOrLabelId: MatchOrLabelId) def withInstId(instId: InstantiationId): RestFunId = matchOrLabelId match case l: LabelSymbol => l -> instId - case scrutId: ResultId => ConcreteId(scrutId, instId) + case scrutId: ResultId @unchecked => ConcreteId(scrutId, instId) extension (vs: Ls[VarSymbol]) def asParamList: ParamList = ParamList(ParamListFlags.empty, vs.map(Param.simple), N) extension (c: CtorCls) def ctorClsName: String = c match @@ -54,10 +54,10 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): ): (Iterator[Label | Match], Block) = val ctx = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToCtxOfLabel(label) - case dtorId: ResultId => pre.res.matchScrutToCtxOfMatch(dtorId) + case dtorId: ResultId @unchecked => pre.res.matchScrutToCtxOfMatch(dtorId) val simpleRest = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToLabelBlk(label).rest - case dtorId: ResultId => pre.res.matchScrutToMatchBlock(dtorId).rest + case dtorId: ResultId @unchecked => pre.res.matchScrutToMatchBlock(dtorId).rest def it = ctx.iterator .takeWhile: case _: (pre.InCtx.Fn | pre.InCtx.ModCtor | pre.InCtx.Cls | pre.InCtx.ClsPreCtor | pre.InCtx.ClsCtor | pre.InCtx.TopLvl) => false @@ -215,7 +215,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): case label: LabelSymbol => pre.res.labelSymToCtxOfLabel(label).collectFirst: case pre.InCtx.Cls(cls) => cls.isym - case dtorId: ResultId => + case dtorId: ResultId @unchecked => pre.res.matchScrutToCtxOfMatch(dtorId).collectFirst: case pre.InCtx.Cls(cls) => cls.isym new BlockMemberSymbol(restFunName, Nil, true) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index 5528e4a1d6..2756d9d8cc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -275,7 +275,7 @@ class Dtor( case class ConcreteId[A <: OriginId](exprId: A, instId: InstantiationId): def pp(using FlowAnalysis.State): Str = exprId match case (sym: Symbol, idx: Int) => s"${sym.nme}#$idx" - case r: ResultId => s"${r.getResult}" + case r: ResultId @unchecked => s"${r.getResult}" type ConcreteProducer = Ctor @@ -833,12 +833,12 @@ class FlowConstraintsCollector( def paramListFunId(whichParamList: Int): FunId = funLamId match case (sym: Symbol, -1) => (sym, whichParamList) - case lambdaExprId: ResultId => + case lambdaExprId: ResultId @unchecked => assert(whichParamList == 0) lambdaExprId val capturedSyms = funLamId match case (sym: TermSymbol, _) => preAnalyzer.res.capturedVars(sym) - case lamExprId: ResultId => preAnalyzer.res.capturedVars(lamExprId) + case lamExprId: ResultId @unchecked => preAnalyzer.res.capturedVars(lamExprId) case other => lastWords(s"unexpected funLamId shape: $other") val res = freshVar(resName, cc.forFunGroup) params.foreach: diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala index 28694853a0..42b98d4355 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala @@ -119,7 +119,7 @@ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: * @param f The partial function to apply to each node. */ def map[L <: Kind.Complete](f: NonCompositional[K] => Pattern[L]): Pattern[L] = this match - case p: NonCompositional[K] => f(p) + case p: NonCompositional[K] @unchecked => f(p) case And(patterns) => And[L](patterns.map(_.map(f))) case Or(patterns) => Or[L](patterns.map(_.map(f))) case Not(pattern) => Not[L](pattern.map(f)) @@ -135,7 +135,7 @@ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: * a list is merged (for ``And`` and ``Or`` nodes) */ def reduce[A](merge: List[A] => A)(f: PartialFunction[Pattern[K], A]): A = this match - case p: NonCompositional[K] => + case p: NonCompositional[K] @unchecked => if f.isDefinedAt(p) then f(p) else merge(Nil) case And(patterns) => merge(patterns.map(_.reduce(merge)(f))) case Or(patterns) => merge(patterns.map(_.reduce(merge)(f))) From 9b5242ca43d795d9706c9b9149e2130a11a2d189 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 12:36:37 +0800 Subject: [PATCH 05/13] misc: Add `lastWords`/`TODO` to missing match arms --- .../src/main/scala/hkmc2/codegen/DeadParamElim.scala | 1 + .../src/main/scala/hkmc2/codegen/EtaExpansion.scala | 1 + .../src/main/scala/hkmc2/codegen/llir/Builder.scala | 3 +++ .../shared/src/main/scala/hkmc2/invalml/InvalML.scala | 7 +++++++ .../src/main/scala/hkmc2/semantics/BlockImpl.scala | 1 + .../src/main/scala/hkmc2/semantics/Elaborator.scala | 9 +++++++-- .../shared/src/main/scala/hkmc2/semantics/Term.scala | 1 + .../scala/hkmc2/semantics/flow/FlowAnalysis.scala | 11 ++++++++--- .../src/main/scala/hkmc2/semantics/ups/Pattern.scala | 3 +++ .../src/main/scala/hkmc2/syntax/ParseRule.scala | 6 ++++-- hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls | 2 +- 11 files changed, 37 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala index 6ebcb421da..293bc3be7f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala @@ -228,6 +228,7 @@ class Rewrite(val deadParamElimSolver: DeadParamElimSolver)(using Raise): (funToSccRepMap(lastRefedSymbol), funToSccRepMap(refSym)) match case (Some(a), Some(b)) if a is b => instId case _ => instId :+ refId + case _ => lastWords(s"newRefId: impossible InstantiationId shape $instId") end newRefId p match diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala index 39c6e9952d..8f08afc751 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala @@ -45,6 +45,7 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): case (fSym: TermSymbol, whichPl: Int) => val fnDefn = constraintSolver.collector.preAnalyzer.res.funSymToFunDefn(fSym) fnDefn.affineInfo.contains(whichPl) + case (sym, _) => lastWords(s"prodFunIsAffine: expected TermSymbol funId, got $sym") end prodFunIsAffine res match diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala index 70bb4401d2..3e757451ee 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -463,6 +463,7 @@ final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): Node.LetExpr(v, Expr.CtorApp(builtinTuple(elems.length), args), k(v |> sr)) case Record(mut, fields) => bErrStop(msg"Unsupported value: Rcd") case x: Path => bPath(x)(k) + case _ => lastWords(s"bResult: unexpected Result $r") private def bBlockWithEndCont(blk: Block)(k: TrivialExpr => Ctx ?=> Node)(using Ctx)(using Raise, Scope) : Node = bBlock(blk)(k)(End("")) @@ -492,6 +493,8 @@ final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): case (Case.Tup(len, inf), body) => val ctx2 = ctx.addKnownClass(scrut, builtinTuple(len)) (Pat.Class(builtinTuple(len)), bBlock(body)(cont)(nextCont)(using ctx2)) + case (Case.Field(name, safe), body) => + TODO(s"Field pattern not supported in LLIR: $name") val defaultCase = dflt.map(bBlock(_)(cont)(nextCont)(using ctx)) val jpdef = Func( uid.make, diff --git a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala index 87ee65860d..6b4e94cf0f 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala @@ -136,6 +136,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case FunTy(Term.Tup(params), ret, eff) => PolyFunType(params.map { case Fld(_, p, _) => typeAndSubstType(p, !pol) + case spd: Spd => lastWords(s"unexpected spread in function type parameters: $spd") }, typeAndSubstType(ret, pol), eff.map(e => typeAndSubstType(e, pol) match { case t: Type => t case _ => error(msg"Effect cannot be polymorphic." -> ty.toLoc :: Nil) @@ -248,6 +249,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case (res, p: Fld) => val (ty, ctx, eff) = typeCode(p.term) (ty :: res._1, res._2 | ctx, res._3 | eff) + case (_, spd: Spd) => TODO(s"spread arguments in quoted code: $spd") val resTy = freshVar(new TempSymbol(S(app), "app")) constrain(lhsTy, FunType(rhsTy.reverse, resTy, Bot)) // TODO: right (resTy, lhsCtx | rhsCtx, lhsEff | rhsEff) @@ -377,6 +379,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): val (altsTy, altsEff, altCases, fallback) = typeADTMatch(alts, sign) val allEff = scrutineeEff | (consEff | altsEff) (sign.getOrElse(tryMkMono(consTy, cons) | tryMkMono(altsTy, alts)), allEff, sym :: altCases, fallback) + case _ => lastWords(s"unexpected non-ClassLike pattern in ADT match: $pattern") case Split.Let(name, term, tail) => val nestCtx = ctx.nest given InvalCtx = nestCtx @@ -426,6 +429,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case _: Tree.DecLit => InvalCtx.numTy case _: Tree.StrLit => InvalCtx.strTy case _: Tree.UnitLit => InvalCtx.unitTy + case (_: FlatPattern.Tuple) | (_: FlatPattern.Record) => TODO(s"tuple/record patterns in invalml split typing: $pattern") constrain(tryMkMono(scrutineeTy, scrutinee), patTy) val (consTy, consEff) = typeSplit(cons, sign)(using nestCtx1) val (altsTy, altsEff) = typeSplit(alts, sign)(using nestCtx2) @@ -519,6 +523,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case (f: Fld, t) => val (ty, ef) = ascribe(f.term, t) resEff |= ef + case (spd: Spd, _) => TODO(s"spread arguments: $spd") (ret, resEff) case (FunType(params, ret, eff), lhsEff) => app((PolyFunType(params, ret, eff), lhsEff), rhs, t) case (ty: PolyType, eff) => app((instantiate(ty), eff), rhs, t) @@ -527,6 +532,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case f: Fld => val (ty, eff) = typeCheck(f.term) Left(ty) :: Right(eff) :: Nil + case spd: Spd => TODO(s"spread arguments: $spd") .partitionMap(x => x) val effVar = freshVar(new TempSymbol(S(t), "eff")) val retVar = freshVar(new TempSymbol(S(t), "app")) @@ -582,6 +588,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case Wildcard(in: InfVar, _) => in :: Nil case Wildcard(_, out: InfVar) => out :: Nil case v: InfVar => v :: Nil + case other => lastWords(s"unexpected non-InfVar type argument in ADT ctor: $other") }, N, PolyFunType(clsDef.params.params.map { case Param(_, _, S(ty), _) => typeAndSubstType(ty, true)(using map.toMap) case p => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala index 3706f791cb..d0498bcb56 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/BlockImpl.scala @@ -33,6 +33,7 @@ trait BlockImpl(using Elaborator.State): lazy val (headId, headPs) = td.baseHead match case id: Ident => (id, Nil) case App(id: Ident, TyTup(ps)) => (id, ps) + case _ => TODO(s"unexpected ADT head shape: ${td.baseHead}") // Temporarily use `data` annotation to distinguish the following ctors: // - Ctor(...) // - Ctor[...](...) extends ADT[...] diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 39c56551de..2ff0b60775 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -771,6 +771,7 @@ extends Importer with ucs.SplitElaborator: val constraints = tys.flatMap(maybeConstraint) val body = term(rhs) Term.Constrained(constraints, body) + case _ => lastWords(s"Unexpected lambda parameter shape: $lhs") case InfixApp(lhs, Keywrd(Keyword.`as`), rhs) => Term.Asc(subterm(lhs), subterm(rhs)) case InfixApp(lhs, Keywrd(Keyword.`:`), rhs) => @@ -1738,6 +1739,7 @@ extends Importer with ucs.SplitElaborator: ClassDef(owner, Cls, clsSym, sym, tsym, tps, pss, newOf(td), ObjBody(bod), annotations, comp) clsSym.defn = S(cd) cd + case Trt | Mxn => lastWords(s"Unexpected type definition kind here: $k") go(sts, Nil, defn :: acc) case Annotated(annotation, target) :: sts => go(target :: sts, annotations ++ annot(annotation), acc) @@ -2077,7 +2079,8 @@ extends Importer with ucs.SplitElaborator: raise(ErrorReport(msg"Unsupported type parameter ${t.describe}" -> t.toLoc :: Nil)) Nil (vs, ctx ++ vs.map(p => p.sym.name -> p.sym)) - + case _ => lastWords(s"Expected a type-parameter tuple; found ${t.describe}.") + def importFrom(sts: Block): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) val (res, newCtx) = block(sts, hasResult = false) @@ -2158,7 +2161,8 @@ extends Importer with ucs.SplitElaborator: if pol =/= S(false) && ty.isContravariant then changed = true ty.isContravariant = false - // case _ => ??? + case S(decl) => + lastWords(s"VarSymbol ${sym.name} has unexpected declaration: $decl") case N => lastWords(s"VarSymbol ${sym.name} has no declaration") case _ => super.traverseType(pol)(trm) @@ -2194,6 +2198,7 @@ extends Importer with ucs.SplitElaborator: case f: Fld => traverseType(pol)(f.term) f.asc.foreach(traverseType(pol)) + case _: Spd => TODO("variance traversal of spread elements") def traverseType(pol: Pol)(f: Param): Unit = f.sign.foreach(traverseType(pol)) end Elaborator diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index c434018d0c..7f304a3301 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -127,6 +127,7 @@ sealed trait ResolvableImpl: case t: Term.Sel => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) case t: Term.SynthSel => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) case t: Term.SelProj => t.copy()(S(sym), t.resSym, t.typ, t.originalCtx) + case _ => lastWords(s"withSym called on non-selection term: $this") .withLocOf(this) .asInstanceOf diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index a4902a4180..93e7a24644 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -68,7 +68,8 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case cls: ClassSymbol => P.Ctor(cls, Nil)(t) case cls: ModuleOrObjectSymbol => P.Ctor(cls, Nil)(t) case ts: TermSymbol => getFlowSymOrType(ts.bms.get) - + case _ => lastWords(s"Unexpected resolved symbol in producer position: $sym") + case Ref(sym) => sym match case sym: VarSymbol => P.Flow(sym) @@ -94,6 +95,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): stmt.sym match case sym: FlowSymbol => constrain(rhs, C.Flow(sym)) + case _ => lastWords(s"Unexpected DefineVar symbol: ${stmt.sym}") case t: TermDefinition => val sign_ty = t.sign.map(typeType(_)) // TODO use sign_ty val ps = t.params.map(typeParamList) @@ -181,6 +183,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case sym: ClassSymbol => val args_t = args.map(typeProd(_, insideSelAppChain = insideSelAppChain)) P.Ctor(sym, args_t)(t) + case S(_) => TODO("New with refinement not yet supported in flow analysis") case app @ App(lhs, rhs) => checkLDS(lhs): pre_t => @@ -200,12 +203,14 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case Tup(fields) => P.Tup(fields.map: - case f: Fld => N -> typeProd(f.term)) + case f: Fld => N -> typeProd(f.term) + case s: Spd => TODO("tuple spread in flow analysis") + ) case Error => P.Ctor(Extr(false), Nil)(t) - // case _ => P.Flow(FlowSymbol("TODO")) + case _ => TODO(t) /* def getType(t: Term): Type = diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala index 42b98d4355..2a20332945 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala @@ -242,6 +242,7 @@ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: case pattern: Record => pattern case pattern: Tuple => pattern case pattern: Literal => pattern + case _: MatchedClassLike => lastWords("MatchedClassLike encountered during expansion") /** Unwrap `Rename` patterns until we reach a non-`Rename` pattern. Collect * the symbols on the way. Maybe we can make `Rename` a property of each @@ -400,6 +401,7 @@ extension (pattern: ExPat) case Literal(`lit`) => Wildcard case _: (Literal | ClassLike) => Never case pattern: (Record | Tuple) => pattern + case _: (MatchedClassLike | Synonym) => lastWords("unexpected specialized/complete node in specialize(lit)") /** Modifies the pattern under the assumption that the scrutinee matches the * given class. */ @@ -421,3 +423,4 @@ extension (pattern: ExPat) case None => pattern.map: case _: (Literal | ClassLike) => Never case pattern: (Record | Tuple) => pattern + case _: (MatchedClassLike | Synonym) => lastWords("unexpected specialized/complete node in specialize(None)") diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala index 681d087ba2..13db5d0a4a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala @@ -256,8 +256,10 @@ class ParseRules(using State): keepKw(`new`): val withRefinement = discardKw(`with`)( ParseRule("'new' body")( - Blk(ParseRule("'new' expression")(end(()))) { case (res: Block // FIXME: can it be something else? - , ()) => S(res) } + Blk(ParseRule("'new' expression")(end(()))) { + case (res: Block, ()) => S(res) // FIXME: can it be something else? + case (res, ()) => lastWords(s"expected a Block from the block parse rule; got ${res.describe}") + } ) ) ParseRule("`new` keyword")( diff --git a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls index d599027cca..d3781622fc 100644 --- a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls +++ b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls @@ -110,7 +110,7 @@ f(1, 2) :fixme f(...a) -//│ /!!!\ Uncaught error: scala.MatchError: Spd(Eager,Ref(a)) (of class hkmc2.semantics.Spd) +//│ /!!!\ Uncaught error: scala.NotImplementedError: tuple spread in flow analysis (of class String) fun przd[A, B](x: A) = x From b130873a97fd8486ea2079e75557ba14394d4301 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 12:59:16 +0800 Subject: [PATCH 06/13] misc: Implement some missing match arms --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 5 +++++ .../scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala | 1 + .../src/main/scala/hkmc2/codegen/llir/Analysis.scala | 1 + .../src/main/scala/hkmc2/codegen/llir/Builder.scala | 1 + hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala | 3 +++ hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala | 8 ++++++-- hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala | 1 + 7 files changed, 18 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index 43b7cc95e4..e11b3df5e1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -93,6 +93,11 @@ sealed abstract class Block extends Product: case Define(defn, rest) => s"""|Define(${defn.showDbg}) |${rest.showDbg}""".stripMargin + case Throw(exc) => s"Throw(${exc.showDbg})" + case Scoped(syms, body) => + s"""|Scoped(${syms.toList.map(_.showDbg).sorted.mkString(", ")}) { + |${body.showDbg} + |}""".stripMargin lazy val isAbortive: Bool = this match case _: End => false diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index 2756d9d8cc..ec7ad3405c 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -836,6 +836,7 @@ class FlowConstraintsCollector( case lambdaExprId: ResultId @unchecked => assert(whichParamList == 0) lambdaExprId + case other => lastWords(s"unexpected funLamId shape: $other") val capturedSyms = funLamId match case (sym: TermSymbol, _) => preAnalyzer.res.capturedVars(sym) case lamExprId: ResultId @unchecked => preAnalyzer.res.capturedVars(lamExprId) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Analysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Analysis.scala index befe5ac59a..b0698a391e 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Analysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Analysis.scala @@ -59,6 +59,7 @@ class UsefulnessAnalysis(verbose: Bool = false): case LetMethodCall(names, cls, method, args, body) => addUse(method); args.foreach(f); names.foreach(addDef); f(body) case LetExpr(name, expr, body) => f(expr); addDef(name); f(body) case LetCall(names, defn, args, body) => args.foreach(f); names.foreach(addDef); f(body) + case Panic(_) => def run(x: Func) = x.params.foreach(addDef) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala index 3e757451ee..fd7d7a78c1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/llir/Builder.scala @@ -598,6 +598,7 @@ final class LlirBuilder(using Elaborator.State)(tl: TraceLogger, uid: FreshInt): case Define(defn, rest) => applyDefn(defn); applyBlock(rest) case Scoped(_, body) => applyBlock(body) case End(msg) => + case Unreachable(_) => override def applyDefn(defn: Defn): Unit = defn match case f: FunDefn => applyFunDefn(f) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala index 1384866505..38e006a0f1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Pattern.scala @@ -131,6 +131,9 @@ object Pattern: case Wildcard() => msg"This variable cannot be accessed." -> id.toLoc case _: Pattern => msg"This pattern cannot be bound." -> pattern.toLoc, msg"Because the pattern it belongs to is negated." -> negation.toLoc) + case (Alias(_, id), Escaped(p)) => error( + msg"This pattern variable escapes its higher-order pattern argument." -> id.toLoc, + msg"It is bound in this pattern." -> p.toLoc) object Variables: lazy val empty: Variables = Variables(HashMap.empty, Nil) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala index 7f304a3301..5bbc12f5e8 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala @@ -38,16 +38,17 @@ enum Annot extends AutoLocated: def subTerms: Vector[Term] = this match case Trm(trm) => Vector.single(trm) - case _: Modifier | Untyped | TailRec | TailCall | Inline | _: Config => Vector.empty + case _: Modifier | Untyped | TailRec | TailCall | Inline | _: Config | _: Affine => Vector.empty def children: Vector[Located] = this match case Trm(trm) => Vector.single(trm) - case _: Modifier | Untyped | TailRec | TailCall | Inline | _: Config => Vector.empty + case _: Modifier | Untyped | TailRec | TailCall | Inline | _: Config | _: Affine => Vector.empty def show(using Scope, ShowCfg, Raise): Document = this match case Untyped => doc"@untyped" case Inline => doc"@inline" case TailRec => doc"@tailrec" + case TailCall => doc"@tailcall" case Affine(n) => doc"@affine($n)" case Modifier(mod) => doc"@${mod.name}" case Trm(trm) => doc"@${trm.show}" @@ -61,6 +62,7 @@ enum Annot extends AutoLocated: case TailCall => TailCall case Inline => Inline case c: Config => c + case a: Affine => a object Annot: @@ -473,6 +475,7 @@ enum Term extends Statement: case term @ TyApp(lhs, targs) => TyApp(lhs.mkClone, targs.map(_.mkClone))(term.typ) case term @ Sel(prefix, nme) => Sel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) case term @ SynthSel(prefix, nme) => SynthSel(prefix.mkClone, Tree.Ident(nme.name))(term.sym, term.resSym, term.typ, term.originalCtx) + case term @ LeadingDotSel(nme) => LeadingDotSel(Tree.Ident(nme.name))(term.originalCtx) case DynSel(prefix, fld, arrayIdx) => DynSel(prefix.mkClone, fld.mkClone, arrayIdx) case term @ Tup(fields) => Tup(fields.map { case f: Fld => f.copy(term = f.term.mkClone, asc = f.asc.map(_.mkClone)) @@ -925,6 +928,7 @@ sealed trait Statement extends AutoLocated, ProductWithExtraInfo: s"type ${sym}${tparams.mkStringOr(", ", "[", "]")} = ${rhs.fold("")(x => x.showDbg)}" case Missing => "missing" case LeadingDotSel(nme) => s"_?_.${nme.name}" + case SetConfig(_) => "#config(...)" final case class LetDecl(sym: LocalSymbol, annotations: Ls[Annot]) extends Statement diff --git a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala index bf418efc04..23ee213555 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala @@ -226,6 +226,7 @@ enum Tree extends AutoLocated: case SplitPoint() => "split point" case OpSplit(lhs, ops_rhss) => "operator split" case OpenIn(opened, body) => "open-in" + case Assert(_, _, _, _) => "assertion" def deparenthesized: Tree = this match case Bra(BracketKind.Round, inner) => inner.deparenthesized From ab1ea34e1786c311819d394eac929d82e4308fa8 Mon Sep 17 00:00:00 2001 From: David Mak Date: Wed, 27 May 2026 14:27:56 +0800 Subject: [PATCH 07/13] misc: Implement the rest of the match arms --- .../main/scala/hkmc2/codegen/Lowering.scala | 3 +- .../main/scala/hkmc2/invalml/InvalML.scala | 7 +- .../scala/hkmc2/semantics/Elaborator.scala | 33 +++++--- .../main/scala/hkmc2/semantics/Resolver.scala | 1 + .../hkmc2/semantics/flow/FlowAnalysis.scala | 4 + .../hkmc2/semantics/ups/Instantiator.scala | 3 + .../backlog/DestructuringBindings.mls | 17 ++-- .../src/test/mlscript/ups/BadPatterns.mls | 9 ++- hkmc2/shared/src/test/mlscript/ups/Future.mls | 77 +++++++++++-------- 9 files changed, 100 insertions(+), 54 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index c10f08baa8..98ba2f13ec 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -962,6 +962,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): setupSelection(prefix, proj, N)(k) case Resolved(sp @ SelProj(prefix, _, proj), sym) => setupSelection(prefix, proj, S(sym))(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), Nil :: Nil), @@ -991,7 +992,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): msg"Cannot compile ${t.describe} term that was not elaborated (maybe elaboration was one in 'lightweight' mode?)" -> t.toLoc :: Nil, source = Diagnostic.Source.Compilation) - case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel + case _: CompType | _: Neg | _: Term.FunTy | _: Term.Forall | _: Term.WildcardTy | _: Term.Unquoted | _: LeadingDotSel | _: Term.Constrained | _: Term.Annotated => fail: ErrorReport( msg"Unexpected term form in expression position (${t.describe})" -> diff --git a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala index 6b4e94cf0f..955c4d625d 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/invalml/InvalML.scala @@ -181,7 +181,7 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case _ => ty.symbol.flatMap(_.asTpe) match case S(cls: (ClassSymbol | TypeAliasSymbol)) => typeAndSubstType(Term.TyApp(ty, Nil)(N), pol) - case N => error(msg"Invalid type" -> ty.toLoc :: Nil, S(ty)) // TODO + case S(_) | N => error(msg"Invalid type" -> ty.toLoc :: Nil, S(ty)) // TODO private def genPolyType(tvs: Ls[QuantVar], outer: InfVar, body: => GeneralType)(using ctx: InvalCtx, cctx: CCtx) = val bds = tvs.map: @@ -314,6 +314,9 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): case L(N) => rec(alts, L(S(sym))) case L(S(other)) if adtParent.get(other.uid).exists(p => p.uid == adtParent(sym.uid).uid) => rec(alts, L(S(sym))) + case L(S(_)) => + error(msg"Matching patterns from different ADTs in one match is not supported." -> split.toLoc :: Nil) + false case R(_) => error(msg"Mixing ADT pattern matching and general matching is not supported yet." -> split.toLoc :: Nil) false @@ -375,6 +378,8 @@ class InvalTyper(using elState: Elaborator.State, tl: TL)(using Ctx): params.iterator.zip(paramList).foreach: case (p, Param(_, _, S(ty), _)) => nestCtx += p._1 -> typeAndSubstType(ty, true)(using map.toMap) + case (_, p) => + error(msg"Invalid ADT parameter." -> p.toLoc :: Nil) val (consTy, consEff) = typeAllSplits(cons, sign)(using nestCtx) val (altsTy, altsEff, altCases, fallback) = typeADTMatch(alts, sign) val allEff = scrutineeEff | (consEff | altsEff) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 2ff0b60775..dfb5a605dc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -1139,8 +1139,11 @@ extends Importer with ucs.SplitElaborator: case Constructor(delc) => raise(ErrorReport(msg"Unsupported constructor in this position." -> tree.toLoc :: Nil)) Term.Error - // case _ => - // ??? + case Dummy | SplitPoint() | Pun(_, _) => + lastWords(s"Unexpected ${tree.describe} in subterm position: $tree") + case _ => + raise(ErrorReport(msg"Unsupported term in this position (${tree.describe})." -> tree.toLoc :: Nil)) + Term.Error def arg(tree: Tree)(using UnderCtx): Ctxl[Term] = tree match case u: Under => subterm(tree) // Note: currently `f(a, _, c)` is treated the same as `f of a, _, c` @@ -1409,6 +1412,11 @@ extends Importer with ucs.SplitElaborator: case S(elem) => elem.symbol match case S(sym: LocalSymbol) => go(sts, Nil, DefineVar(sym, r) :: acc) + case S(sym) => + raise(ErrorReport(msg"Cannot assign to '${id.name}'." -> id.toLoc :: Nil)) + go(sts, Nil, Term.Error :: acc) + case N => + lastWords(s"Bound name '${id.name}' has no symbol") case N => // TODO lookup in members? inherited/refined stuff? raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) @@ -1526,17 +1534,21 @@ extends Importer with ucs.SplitElaborator: val tps = td.typeParams match case S(ts) => ts.tys.flatMap: targ => - val (id, vce) = targ match + def mk(id: Ident, vce: Opt[Bool]): Ls[TyParam] = + val vs = VarSymbol(id) + val res = TyParam(FldFlags.empty, vce, vs) + vs.decl = S(res) + res :: Nil + targ match case id: Ident => - (id, N) + mk(id, N) case Modified(Keywrd(Keyword.`in`), id: Ident) => - (id, S(false)) + mk(id, S(false)) case Modified(Keywrd(Keyword.`out`), id: Ident) => - (id, S(true)) - val vs = VarSymbol(id) - val res = TyParam(FldFlags.empty, vce, vs) - vs.decl = S(res) - res :: Nil + mk(id, S(true)) + case _ => + raise(ErrorReport(msg"Unsupported type parameter ${targ.describe}" -> targ.toLoc :: Nil)) + Nil case N => Nil newCtx ++= tps.map(tp => tp.sym.name -> tp.sym) // TODO: correct ++? @@ -1874,6 +1886,7 @@ extends Importer with ucs.SplitElaborator: case N => go(tl, p :: acc, newCtx, newFlags) case L(d) => raise(d); go(tl, acc, ctx, flags) go(ps, Nil, ctx, ParamListFlags.empty) + case _ => lastWords(s"Expected a parameter list (Tup); found ${t.describe}") 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/Resolver.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala index 3c6d4232d4..de78453a53 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Resolver.scala @@ -941,6 +941,7 @@ class Resolver(tl: TraceLogger) case _: Class => bms.asCls case _: Selectable => bms.asModOrObj orElse bms.asTrm case _: (Any.type | NonModule) => bms.asPrincipal + case _: PatternConstructor => TODO("disambSym for PatternConstructor") t match case Term.New(cls, _, N) => cls.resolvedSym match diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index 93e7a24644..9caef3d0b0 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -406,6 +406,10 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case N => raise(ErrorReport( msg"Tuple arity mismatch: too many elements on the producer side" -> trm.toLoc :: Nil)) + case (Nil, _ :: _) => + raise(ErrorReport( + msg"Tuple arity mismatch: too few elements on the producer side" -> trm.toLoc :: Nil)) + case ((S(_), _) :: _, _ :: _) => ??? // TODO: producer-side spread vs remaining consumers zip(args, ini, rst, path) case (sel @ P.LeadingDotSel(trm), rhs) => rhs match case C.Typ(Type.Ref(sym, _)) => diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala index 81b85b218b..ac0e0672f4 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Instantiator.scala @@ -116,6 +116,8 @@ class Instantiator(using tl: TL)(using Ctx, State, Raise): val patternArguments = arguments.getOrElse(Nil).map(instantiate(_)) val instantiation = Instantiation(symbol, patternArguments)(pattern.toLoc) Synonym(schedule(instantiation)) + case N => lastWords(s"Expected target symbol to be a Class-like Symbol, got ${symbol.getClass.getSimpleName}") + case N => lastWords(s"Missing symbol for constructor pattern `${target.showAsTree}`") case SP.Composition(true, left, right) => instantiate(left) or instantiate(right) case SP.Composition(false, left, right) => instantiate(left) and instantiate(right) case SP.Negation(pattern) => Not(instantiate(pattern)) @@ -169,3 +171,4 @@ class Instantiator(using tl: TL)(using Ctx, State, Raise): acc case N => true instantiate(pattern) + case _: SP.Guarded => TODO("instantiate for Guarded") diff --git a/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls b/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls index 3a3d41169b..7d2d0db58f 100644 --- a/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls +++ b/hkmc2/shared/src/test/mlscript/backlog/DestructuringBindings.mls @@ -15,26 +15,29 @@ //│ ╔══[COMPILATION ERROR] Name not found: a //│ ║ l.5: [a, b] => [b, a] //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.MatchError: Constrained(List(),Tup(List(Fld(‹›,Error,None), Fld(‹›,Error,None)))) (of class hkmc2.semantics.Term$Constrained) +//│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (constrained type) +//│ ║ l.5: [a, b] => [b, a] +//│ ╙── ^^^^^^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found tuple -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^^^^^^ //│ ╔══[COMPILATION ERROR] Name not found: a -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: c -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: b -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ //│ ╔══[COMPILATION ERROR] Name not found: d -//│ ║ l.20: fun eqTup2([a, b], [c, d]) = a == c and b == d +//│ ║ l.23: fun eqTup2([a, b], [c, d]) = a == c and b == d //│ ╙── ^ diff --git a/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls b/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls index d1dd439f55..2aa64eff58 100644 --- a/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls +++ b/hkmc2/shared/src/test/mlscript/ups/BadPatterns.mls @@ -15,12 +15,17 @@ pattern Foo1[A]: ... :fixme pattern Foo2[pattern A]: ... -//│ /!!!\ Uncaught error: scala.MatchError: TypeDef(Pat,Ident(A),None) (of class hkmc2.syntax.Tree$TypeDef) +//│ ╔══[COMPILATION ERROR] Unsupported type parameter type definition +//│ ║ l.17: pattern Foo2[pattern A]: ... +//│ ╙── ^^^^^^^^^ +//│ ╔══[COMPILATION ERROR] Pattern definitions must have a body. +//│ ║ l.17: pattern Foo2[pattern A]: ... +//│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :e pattern Foo(pattern T) //│ ╔══[COMPILATION ERROR] Pattern definitions must have a body. -//│ ║ l.21: pattern Foo(pattern T) +//│ ║ l.26: pattern Foo(pattern T) //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/ups/Future.mls b/hkmc2/shared/src/test/mlscript/ups/Future.mls index 5644d14e16..70a77c3da6 100644 --- a/hkmc2/shared/src/test/mlscript/ups/Future.mls +++ b/hkmc2/shared/src/test/mlscript/ups/Future.mls @@ -14,25 +14,33 @@ pattern Rep0[A] = "" | A ~ Rep0[A] // Hypothetical syntax: put pattern parameters in the type parameter list but // prefixed with `pattern`. pattern Rep0[pattern A] = "" | A ~ Rep0[A] -//│ /!!!\ Uncaught error: scala.MatchError: TypeDef(Pat,Ident(A),None) (of class hkmc2.syntax.Tree$TypeDef) +//│ ╔══[COMPILATION ERROR] Unsupported type parameter type definition +//│ ║ l.16: pattern Rep0[pattern A] = "" | A ~ Rep0[A] +//│ ╙── ^^^^^^^^^ +//│ ╔══[COMPILATION ERROR] Pattern name not found: A. +//│ ║ l.16: pattern Rep0[pattern A] = "" | A ~ Rep0[A] +//│ ╙── ^ +//│ ╔══[COMPILATION ERROR] Unrecognized pattern (application). +//│ ║ l.16: pattern Rep0[pattern A] = "" | A ~ Rep0[A] +//│ ╙── ^^^^^^^ :e // Hypothetical syntax: separate parameter list for the extraction. pattern Rep0(pattern A, B, C)(head) = "" | (A as head) ~ Rep0[A] //│ ╔══[COMPILATION ERROR] Multiple parameter lists are not supported for this definition. -//│ ║ l.21: pattern Rep0(pattern A, B, C)(head) = +//│ ║ l.29: pattern Rep0(pattern A, B, C)(head) = //│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.22: "" | (A as head) ~ Rep0[A] +//│ ║ l.30: "" | (A as head) ~ Rep0[A] //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (application). -//│ ║ l.22: "" | (A as head) ~ Rep0[A] +//│ ║ l.30: "" | (A as head) ~ Rep0[A] //│ ╙── ^^^^^^^ //│ ╔══[COMPILATION ERROR] Found an inconsistent variable in disjunction patterns. -//│ ║ l.22: "" | (A as head) ~ Rep0[A] +//│ ║ l.30: "" | (A as head) ~ Rep0[A] //│ ║ ^^^^ //│ ╟── The variable is missing from this sub-pattern. -//│ ║ l.22: "" | (A as head) ~ Rep0[A] +//│ ║ l.30: "" | (A as head) ~ Rep0[A] //│ ╙── ^^ pattern Identifier = ("a" ..= "z") ~ (Identifier | "") @@ -46,7 +54,7 @@ pattern Email(name, domain) = pattern GreaterThan(value) = case n and n > value then n //│ ╔══[COMPILATION ERROR] Unrecognized pattern (case). -//│ ║ l.47: n and n > value then n +//│ ║ l.55: n and n > value then n //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^ :e @@ -59,11 +67,11 @@ fun foo(x) = if x is Unit then .... Arrow(...) then .... //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator 'as'). -//│ ║ l.58: view as +//│ ║ l.66: view as //│ ║ ^^^^^^^ -//│ ║ l.59: Unit then .... +//│ ║ l.67: Unit then .... //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.60: Arrow(...) then .... +//│ ║ l.68: Arrow(...) then .... //│ ╙── ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,10 +89,10 @@ pattern Star(pattern T) = "" | Many(T) pattern Email(name, domains) = Rep(Char | ".") as name ~ "@" ~ Rep(Rep(Char) ~ ) as domain //│ ╔══[PARSE ERROR] Expected end of input; found literal instead -//│ ║ l.78: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" +//│ ║ l.86: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" //│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (juxtaposition). -//│ ║ l.78: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" +//│ ║ l.86: pattern Char = "a" to "z" | "A" to "Z" | "0" to "9" | "_" | "-" //│ ╙── ^^^^^^ :pe @@ -92,11 +100,11 @@ pattern Email(name, domains) = // Hypothetical syntax: use `to` instead of ugly `..=` pattern Digits = "0" to "9" ~ (Digits | "") //│ ╔══[PARSE ERROR] Expected end of input; found literal instead -//│ ║ l.93: pattern Digits = "0" to "9" ~ (Digits | "") -//│ ╙── ^^^ +//│ ║ l.101: pattern Digits = "0" to "9" ~ (Digits | "") +//│ ╙── ^^^ //│ ╔══[COMPILATION ERROR] Unrecognized pattern (juxtaposition). -//│ ║ l.93: pattern Digits = "0" to "9" ~ (Digits | "") -//│ ╙── ^^^^^^ +//│ ║ l.101: pattern Digits = "0" to "9" ~ (Digits | "") +//│ ╙── ^^^^^^ pattern Test(foo, bar) = ("foo" as foo) ~ ("bar" as bar) @@ -130,11 +138,11 @@ x => if x as 0 then false 1 then true //│ ╔══[COMPILATION ERROR] Unrecognized term split (infix operator 'as') -//│ ║ l.129: x => if x as +//│ ║ l.137: x => if x as //│ ║ ^^^^ -//│ ║ l.130: 0 then false +//│ ║ l.138: 0 then false //│ ║ ^^^^^^^^^^^^^^ -//│ ║ l.131: 1 then true +//│ ║ l.139: 1 then true //│ ╙── ^^^^^^^^^^^^^ //│ = fun @@ -145,11 +153,11 @@ fun test(x) = if x is true then "T" false then "F" //│ ╔══[COMPILATION ERROR] Unrecognized pattern split (infix operator 'as'). -//│ ║ l.144: Test as +//│ ║ l.152: Test as //│ ║ ^^^^^^^ -//│ ║ l.145: true then "T" +//│ ║ l.153: true then "T" //│ ║ ^^^^^^^^^^^^^^^^^^ -//│ ║ l.146: false then "F" +//│ ║ l.154: false then "F" //│ ╙── ^^^^^^^^^^^^^^^^^^ @@ -157,7 +165,7 @@ fun test(x) = if x is pattern Test = (0 => false) | (1 => true) if 0 is ((Test as r) where (r is x)) then print(x) //│ ╔══[COMPILATION ERROR] Name not found: x -//│ ║ l.158: if 0 is ((Test as r) where (r is x)) then print(x) +//│ ║ l.166: if 0 is ((Test as r) where (r is x)) then print(x) //│ ╙── ^ //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. @@ -166,14 +174,14 @@ if 0 is ((Test as r) where (r is x)) then print(x) pattern Test = 0 | 1 class Id(name: Test) //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. -//│ ║ l.167: class Id(name: Test) +//│ ║ l.175: class Id(name: Test) //│ ╙── ^^^^ //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. -//│ ║ l.167: class Id(name: Test) +//│ ║ l.175: class Id(name: Test) //│ ║ ^^^^ //│ ╙── Non-module parameter must have a non-module type. //│ ╔══[COMPILATION ERROR] Expected a non-module type; found reference. -//│ ║ l.167: class Id(name: Test) +//│ ║ l.175: class Id(name: Test) //│ ╙── ^^^^ @@ -185,13 +193,13 @@ pattern StatementBased = 1 => "one" _ => "many" //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. -//│ ║ l.184: 0 => "zero" +//│ ║ l.192: 0 => "zero" //│ ╙── ^^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. -//│ ║ l.185: 1 => "one" +//│ ║ l.193: 1 => "one" //│ ╙── ^^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Unexpected record property pattern. -//│ ║ l.186: _ => "many" +//│ ║ l.194: _ => "many" //│ ╙── ^^^^^^^^^^^ @@ -202,16 +210,19 @@ pattern StatementBased = :fixme // TODO: support lambdas with patterns defined this way? | 0 => 1 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal -//│ ║ l.203: | 0 => 1 +//│ ║ l.211: | 0 => 1 //│ ╙── ^ //│ = fun :fixme // TODO | [x] => 1 //│ ╔══[COMPILATION ERROR] Illegal constraint syntax. -//│ ║ l.210: | [x] => 1 +//│ ║ l.218: | [x] => 1 //│ ╙── ^ -//│ /!!!\ Uncaught error: scala.MatchError: Constrained(List(),Lit(IntLit(1))) (of class hkmc2.semantics.Term$Constrained) +//│ ╔══[COMPILATION ERROR] Unexpected term form in expression position (constrained type) +//│ ║ l.218: | [x] => 1 +//│ ╙── ^ +//│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. | (x, y) => 1 //│ = fun @@ -219,7 +230,7 @@ pattern StatementBased = :fixme // TODO | (x, 0) => 1 //│ ╔══[COMPILATION ERROR] Expected a valid parameter, found integer literal -//│ ║ l.220: | (x, 0) => 1 +//│ ║ l.231: | (x, 0) => 1 //│ ╙── ^ //│ = fun From 8ee8e501966a37e5f2c5672d890e732778a7878c Mon Sep 17 00:00:00 2001 From: David Mak Date: Mon, 1 Jun 2026 12:27:29 +0800 Subject: [PATCH 08/13] Add `SplitSymbol` to `Value.SimpleRef` and refactor --- hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala | 4 ++-- hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala | 6 +++--- .../src/main/scala/hkmc2/semantics/ucs/Normalization.scala | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala index e11b3df5e1..2ddaaeba84 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala @@ -1008,7 +1008,7 @@ case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[DefinitionSymbol case class DynSelect(qual: Path, fld: Path, arrayIdx: Bool) extends Path enum Value extends Path with ProductWithExtraInfo: - case SimpleRef(sym: LocalVarSymbol | BuiltinSymbol) + case SimpleRef(sym: LocalVarSymbol | BuiltinSymbol | SplitSymbol) /** * @param disamb The symbol disambiguating the definition that the reference refers to. */ @@ -1089,7 +1089,7 @@ extension (k: Block => Block) def blockBuilder: Block => Block = identity -extension (s: (LocalVarSymbol | BuiltinSymbol)) +extension (s: (LocalVarSymbol | BuiltinSymbol | SplitSymbol)) inline def asSimpleRef: Value.SimpleRef = Value.SimpleRef(s) extension (bms: BlockMemberSymbol) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index ce93a3188e..ba3712e944 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -1057,11 +1057,11 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): loweringCtx.collectScopedSym(sym) val l1, l2, l3 = loweringCtx.registerTempSymbol(N) blockBuilder.assign(l1, r1) - .chain(b => Assign(sym, Value.Ref(l1), b)) + .chain(b => Assign(sym, l1.asSimpleRef, b)) .chain(b => quoteSplit(sym.body)(r2 => Assign(l2, r2, b))) .chain(b => quoteSplit(tail)(r3 => Assign(l3, r3, b))) - .rest(setupTerm("LetSplit", (l1 :: l2 :: l3 :: Nil).map(s => Value.Ref(s)))(k)) - case Split.UseSplit(sym) => setupTerm("UseSplit", Value.Ref(sym, N) :: Nil)(k) + .rest(setupTerm("LetSplit", (l1 :: l2 :: l3 :: Nil).map(_.asSimpleRef))(k)) + case Split.UseSplit(sym) => setupTerm("UseSplit", sym.asSimpleRef :: Nil)(k) lazy val setupFilename: Path = val state = summon[State] 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 98424e1d2d..2ebbbe8e18 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala @@ -404,7 +404,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State, C val exitCont: Result => Block = r => Assign(tmp, r, Break(exitLabel)) val bodyBlock = lowerSplit(sym.body, exitCont) val tailBlock = lowerSplit(tail, exitCont) - Label(exitLabel, false, Label(joinLabel, false, tailBlock, bodyBlock), cont(Value.Ref(tmp))) + Label(exitLabel, false, Label(joinLabel, false, tailBlock, bodyBlock), cont(tmp.asSimpleRef)) case Split.UseSplit(sym) => sym.label match case S(label) => Break(label) From c2566f397f0246a152bef75032e0396854c0b956 Mon Sep 17 00:00:00 2001 From: David Mak Date: Mon, 1 Jun 2026 12:46:54 +0800 Subject: [PATCH 09/13] Fix newly introduced warnings --- hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala | 2 -- .../shared/src/main/scala/hkmc2/semantics/Elaborator.scala | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index 65989d1661..2100e3cc0a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -1072,8 +1072,6 @@ class Lowering()(using Config, TL, Raise, State, Ctx, SymbolPrinter): .chain(b => quote(term)(r2 => Assign(l2, r2, b))) .chain(b => quoteSplit(tail, splitTmps)(r3 => Assign(l3, r3, b))) .rest(setupTerm("Let", (l1 :: l2 :: l3 :: Nil).map(s => s.asSimpleRef))(k)) - case Split.Let(sym, _, _) => - lastWords(s"tried to quote split let with non-variable symbol ${sym.nme}") case Split.Else(default) => quote(default): r => val l = loweringCtx.registerTempSymbol(N) Assign(l, r, setupTerm("Else", l.asSimpleRef :: Nil)(k)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 4d2a77d942..80973ed136 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -1442,6 +1442,12 @@ extends Importer with ucs.SplitElaborator: case S(elem) => elem.symbol match case S(sym: (LocalSymbol | TermSymbol)) => go(sts, Nil, DefineVar(sym, r) :: 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 :: acc) + case N => + raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) + go(sts, Nil, Term.Error :: acc) case N => // TODO lookup in members? inherited/refined stuff? raise(ErrorReport(msg"Name not found: ${id.name}" -> id.toLoc :: Nil)) From 28854fcbebeecace5ce9aa9885c2a8a3bc395157 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Fri, 5 Jun 2026 11:52:48 +0800 Subject: [PATCH 10/13] Remove `ResultId @unchecked` --- .../main/scala/hkmc2/codegen/DeadParamElim.scala | 2 +- .../main/scala/hkmc2/codegen/EtaExpansion.scala | 8 ++++---- .../scala/hkmc2/codegen/deforest/Deforest.scala | 2 +- .../scala/hkmc2/codegen/deforest/Rewrite.scala | 10 +++++----- .../codegen/flowAnalysis/FlowAnalysis.scala | 16 +++++++++------- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala index 322798840b..4a9a48c710 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/DeadParamElim.scala @@ -119,7 +119,7 @@ class DeadParamElimSolver(val constraintSolver: FlowConstraintSolver): def showProdFun(prodFun: ProdFun): Str = def showFunId(funId: FunId): Str = funId match case (funSym: Symbol, whichParamList) => s"${funSym.nme}#$whichParamList" - case exprId: ResultId @unchecked => exprId.getResult match + case exprId: ResultId => exprId.getResult match case Lambda(_, _) => s"lambda@$exprId" case _ => showRefSite(exprId) val inst = prodFun.instantiationId.fold("")(instId => s" @ ${showInstId(instId)}") diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala index 8f08afc751..fe2e217b32 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/EtaExpansion.scala @@ -38,7 +38,7 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): def prodFunIsAffine(pf: ProdFun) = pf.exprId match - case lamId: ResultId @unchecked => + case lamId: ResultId => lamId.getResult match case lamDef: Lambda => lamDef.affine case other => lastWords(s"expected lambda result, got $other") @@ -100,14 +100,14 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): for pf <- constraintSolver.funDests.keysIterator do pf.exprId match - case lamId: ResultId @unchecked => funShape.getOrElseUpdate(lamId, etaExpansionTargetShapes(pf)(using Set.empty)) + case lamId: ResultId => funShape.getOrElseUpdate(lamId, etaExpansionTargetShapes(pf)(using Set.empty)) case (funSym: TermSymbol, 0) => funShape.getOrElseUpdate(funSym, etaExpansionTargetShapes(pf)(using Set.empty)) case _ => () private def showFunShapeId(id: TermSymbol | ResultId): Str = id match case funSym: TermSymbol => funSym.nme - case lamId: ResultId @unchecked => + case lamId: ResultId => lamId.getResult match case Lambda(_, _) => s"lambda@$lamId" case r => lastWords(s"not lambda $r") @@ -120,7 +120,7 @@ class EtaExpansionSolver(val constraintSolver: FlowConstraintSolver): val prev = constraintSolver.preAnalyzer.res.funSymToFunDefn(funSym).params.size val now = shape.size now > prev - case lamId: ResultId @unchecked => shape.size > 1 + case lamId: ResultId => shape.size > 1 def hasEtaExpansionTargets: Bool = funShape.exists: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala index 6b1b4ef37b..db38052403 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Deforest.scala @@ -120,7 +120,7 @@ class DeforestFusionSolver(val constraintSolver: FlowConstraintSolver)(using val dest match case FinalDestMatch(dtor, sels) => tl.log(s"\tmatch: ${pp(dtor)}") - for s <- sels.toSeq.sortBy(_.exprId) do tl.log(s"\tfields: ${pp(s)}") + for s <- sels.toSeq.sortBy(_.exprId.uid) do tl.log(s"\tfields: ${pp(s)}") case FinalDestSel(dtors, field) => tl.log(s"\tselect: ${pp(field)}") tl.log("<<< fusing <<<") 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 e2ba680522..791b724553 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/deforest/Rewrite.scala @@ -36,7 +36,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): extension (matchOrLabelId: MatchOrLabelId) def withInstId(instId: InstantiationId): RestFunId = matchOrLabelId match case l: LabelSymbol => l -> instId - case scrutId: ResultId @unchecked => ConcreteId(scrutId, instId) + case scrutId: ResultId => ConcreteId(scrutId, instId) extension (vs: Ls[VarSymbol]) def asParamList: ParamList = ParamList(ParamListFlags.empty, vs.map(Param.simple), N) extension (c: CtorCls) def ctorClsName: String = c match @@ -51,10 +51,10 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): ): (Iterator[Label | Match], Block) = val ctx = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToCtxOfLabel(label) - case dtorId: ResultId @unchecked => pre.res.matchScrutToCtxOfMatch(dtorId) + case dtorId: ResultId => pre.res.matchScrutToCtxOfMatch(dtorId) val simpleRest = matchOrLabelId match case label: LabelSymbol => pre.res.labelSymToLabelBlk(label).rest - case dtorId: ResultId @unchecked => pre.res.matchScrutToMatchBlock(dtorId).rest + case dtorId: ResultId => pre.res.matchScrutToMatchBlock(dtorId).rest def it = ctx.iterator .takeWhile: case _: (pre.InCtx.Fn | pre.InCtx.ModCtor | pre.InCtx.Cls | pre.InCtx.ClsPreCtor | pre.InCtx.ClsCtor | pre.InCtx.TopLvl) => false @@ -122,7 +122,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): finalDest match case FinalDestSel(dtors, field) => // create poly fun syms - val instIds = (dtors + ctor).toList.sortBy(_.exprId).map(_.instId) + val instIds = (dtors + ctor).toList.sortBy(_.exprId.uid).map(_.instId) for ctorInstId <- instIds case path@(pathTo :+ refedFun) <- ctorInstId.inits @@ -142,7 +142,7 @@ class DeforestRewriter(val solver: DeforestFusionSolver)(using Raise): // create branch sel syms val fieldSym = MutMap.empty[SelField, VarSymbol] - for sel <- sels.toList.sortBy(_._1) do + for sel <- sels.toList.sortBy(_._1.uid) do branchSelSyms.getOrElseUpdate( sel, locally: diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index 9ac4b487cd..cd7c400bd1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -18,8 +18,8 @@ object FlowAnalysis: val AccumulatorSym = "flow-analysis/accumulator" class State: - val resultToResultId = new java.util.IdentityHashMap[Result, Uid[Result]].asScala - val resultIdToResult = mutable.Map.empty[Uid[Result], Result] + val resultToResultId = new java.util.IdentityHashMap[Result, ResultId].asScala + val resultIdToResult = mutable.Map.empty[ResultId, Result] val stratVarIdToState = mutable.Map.empty[StratVarId, StratVarState] object ResultUidState extends Uid.Result.State @@ -46,7 +46,7 @@ object FlowAnalysis: extension (r: Result) def uid = resultToResultId.get(r) match case None => - val id = ResultUidState.nextUid + val id = ResultId(ResultUidState.nextUid) resultIdToResult(id) = r resultToResultId(r) = id id @@ -81,7 +81,9 @@ object FlowAnalysis: end FlowAnalysis -type ResultId = Uid[Result] +class ResultId(val uid: Uid[Result]): + override def toString: String = uid.toString + type InstantiationId = Ls[ResultId] type CtorCls = ClassLikeSymbol | Int type SelField = TermSymbol | Int @@ -256,7 +258,7 @@ class Dtor( case class ConcreteId[A <: OriginId](exprId: A, instId: InstantiationId): def pp(using FlowAnalysis.State): Str = exprId match case (sym: Symbol, idx: Int) => s"${sym.nme}#$idx" - case r: ResultId @unchecked => s"${r.getResult}" + case r: ResultId => s"${r.getResult}" type ConcreteProducer = Ctor @@ -810,13 +812,13 @@ class FlowConstraintsCollector( def paramListFunId(whichParamList: Int): FunId = funLamId match case (sym: Symbol, -1) => (sym, whichParamList) - case lambdaExprId: ResultId @unchecked => + case lambdaExprId: ResultId => assert(whichParamList == 0) lambdaExprId case other => lastWords(s"unexpected funLamId shape: $other") val capturedSyms = funLamId match case (sym: TermSymbol, _) => preAnalyzer.res.capturedVars(sym) - case lamExprId: ResultId @unchecked => preAnalyzer.res.capturedVars(lamExprId) + case lamExprId: ResultId => preAnalyzer.res.capturedVars(lamExprId) case other => lastWords(s"unexpected funLamId shape: $other") val res = freshVar(resName, cc.forFunGroup) params.foreach: From 6f951e45922e573e9567bb28504b4339533d4885 Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Fri, 5 Jun 2026 11:54:09 +0800 Subject: [PATCH 11/13] Remove remaining `@unchecked`s --- .../shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala index 2a20332945..0b1bc72de1 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Pattern.scala @@ -118,8 +118,8 @@ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: * not defined at a node, the node is left unchanged. * @param f The partial function to apply to each node. */ - def map[L <: Kind.Complete](f: NonCompositional[K] => Pattern[L]): Pattern[L] = this match - case p: NonCompositional[K] @unchecked => f(p) + def map[L <: Kind.Complete](f: NonCompositional[? <: K] => Pattern[L]): Pattern[L] = this match + case p: NonCompositional[? <: K] => f(p) case And(patterns) => And[L](patterns.map(_.map(f))) case Or(patterns) => Or[L](patterns.map(_.map(f))) case Not(pattern) => Not[L](pattern.map(f)) @@ -135,7 +135,7 @@ sealed abstract class Pattern[+K <: Kind.Complete] extends AutoLocated: * a list is merged (for ``And`` and ``Or`` nodes) */ def reduce[A](merge: List[A] => A)(f: PartialFunction[Pattern[K], A]): A = this match - case p: NonCompositional[K] @unchecked => + case p: NonCompositional[? <: K] => if f.isDefinedAt(p) then f(p) else merge(Nil) case And(patterns) => merge(patterns.map(_.reduce(merge)(f))) case Or(patterns) => merge(patterns.map(_.reduce(merge)(f))) From 71b36015f4695f7f492342c825c705233acde37f Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Fri, 5 Jun 2026 12:01:06 +0800 Subject: [PATCH 12/13] Make various improvements --- .../codegen/flowAnalysis/FlowAnalysis.scala | 6 +-- .../scala/hkmc2/semantics/Elaborator.scala | 24 +++++++++--- .../hkmc2/semantics/flow/FlowAnalysis.scala | 3 +- .../test/mlscript/basics/BadAssignments.mls | 38 +++++++++++-------- .../src/test/mlscript/basics/BadDefs.mls | 8 ++++ .../src/test/mlscript/flows/BasicFlows.mls | 2 +- 6 files changed, 54 insertions(+), 27 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala index cd7c400bd1..adedd750c2 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/flowAnalysis/FlowAnalysis.scala @@ -87,7 +87,7 @@ class ResultId(val uid: Uid[Result]): type InstantiationId = Ls[ResultId] type CtorCls = ClassLikeSymbol | Int type SelField = TermSymbol | Int -type FunId = (funSym: Symbol, whichParamList: Int) | ResultId +type FunId = (funSym: TermSymbol, whichParamList: Int) | ResultId type OriginId = ResultId | FunId @@ -257,7 +257,7 @@ class Dtor( case class ConcreteId[A <: OriginId](exprId: A, instId: InstantiationId): def pp(using FlowAnalysis.State): Str = exprId match - case (sym: Symbol, idx: Int) => s"${sym.nme}#$idx" + case (sym: TermSymbol, idx: Int) => s"${sym.nme}#$idx" case r: ResultId => s"${r.getResult}" @@ -811,7 +811,7 @@ class FlowConstraintsCollector( )(using cc: ConstraintsCollector): ProdStrat = def paramListFunId(whichParamList: Int): FunId = funLamId match - case (sym: Symbol, -1) => (sym, whichParamList) + case (sym: TermSymbol, -1) => (sym, whichParamList) case lambdaExprId: ResultId => assert(whichParamList == 0) lambdaExprId diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 245044d90b..f189d9c383 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -677,6 +677,10 @@ extends Importer with ucs.SplitElaborator: Term.Assgn(lt, subterm(rhs)) :: Nil, subterm(bod), ), Term.Assgn(lt, sym.ref()))) + case LetLike(Keywrd(Keyword.`set`), _, N, S(_)) => + raise: + ErrorReport(msg"Expected a right-hand side for this assignment" -> tree.toLoc :: Nil) + Term.Error case (hd @ Hndl(id: Ident, c, Block(sts_), S(bod))) => ctx.nest(OuterCtx.LambdaOrHandlerBlock).givenIn: val sym = VarSymbol(id) @@ -1161,9 +1165,9 @@ extends Importer with ucs.SplitElaborator: case Constructor(delc) => raise(ErrorReport(msg"Unsupported constructor in this position." -> tree.toLoc :: Nil)) Term.Error - case Dummy | SplitPoint() | Pun(_, _) => + case Dummy | _: SplitPoint | _: LexicalNew | _: Region | _: Effectful => lastWords(s"Unexpected ${tree.describe} in subterm position: $tree") - case _ => + case Dummy | _: SplitPoint | _: Pun | _: LetLike | _: TyTup | _: Directive => raise(ErrorReport(msg"Unsupported term in this position (${tree.describe})." -> tree.toLoc :: Nil)) Term.Error @@ -1545,7 +1549,7 @@ extends Importer with ucs.SplitElaborator: case (td @ TypeDef(k, head, rhs)) :: sts => val owner = ctx.outer.inner - assert((k is Als) || (k is Cls) || (k is Mod) || (k is Obj) || (k is Pat), k) + softTODO((k is Als) || (k is Cls) || (k is Mod) || (k is Obj) || (k is Pat), k.desc + " not yet supported") val body = td.withPart td.symbName match @@ -1923,7 +1927,7 @@ extends Importer with ucs.SplitElaborator: case "<:<" => SubDir.Sub case ">:>" => SubDir.Sup SubConstraint(l, r, dir) - + /** Elaborate a subtyping constraint that may be malformed. */ def maybeConstraint(t: Tree): Ctxl[Option[SubConstraint]] = t match @@ -1965,7 +1969,11 @@ extends Importer with ucs.SplitElaborator: case N => go(tl, p :: acc, newCtx, newFlags) case L(d) => raise(d); go(tl, acc, ctx, flags) go(ps, Nil, ctx, ParamListFlags.empty) - case _ => lastWords(s"Expected a parameter list (Tup); found ${t.describe}") + case _ => + 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) def ident(id: Ident)(using Ctx): Ctxl[Opt[Term]] = ctx.get(id.name) match case S(elem) => S(elem.ref(id)) @@ -2171,7 +2179,11 @@ extends Importer with ucs.SplitElaborator: raise(ErrorReport(msg"Unsupported type parameter ${t.describe}" -> t.toLoc :: Nil)) Nil (vs, ctx ++ vs.map(p => p.sym.name -> p.sym)) - case _ => lastWords(s"Expected a type-parameter tuple; found ${t.describe}.") + case _ => + raise: + ErrorReport: + msg"Expected a type parameter list (a tuple of identifiers), but found ${t.describe}" -> t.toLoc :: Nil + (Nil, ctx) def importFrom(sts: Block): Ctxl[(Blk, Ctx)] = given UnderCtx = new UnderCtx(N) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala index 9caef3d0b0..62dcf2796b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/flow/FlowAnalysis.scala @@ -69,7 +69,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case cls: ModuleOrObjectSymbol => P.Ctor(cls, Nil)(t) case ts: TermSymbol => getFlowSymOrType(ts.bms.get) case _ => lastWords(s"Unexpected resolved symbol in producer position: $sym") - + case Ref(sym) => sym match case sym: VarSymbol => P.Flow(sym) @@ -210,6 +210,7 @@ class FlowAnalysis(using tl: TraceLogger)(using Raise, State, Ctx): case Error => P.Ctor(Extr(false), Nil)(t) + // case _ => P.Flow(FlowSymbol("TODO")) case _ => TODO(t) /* diff --git a/hkmc2/shared/src/test/mlscript/basics/BadAssignments.mls b/hkmc2/shared/src/test/mlscript/basics/BadAssignments.mls index 2693070bd0..ea82a73751 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadAssignments.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadAssignments.mls @@ -2,11 +2,17 @@ fun x = 1 +:ge +set x +//│ ╔══[COMPILATION ERROR] Expected a right-hand side for this assignment +//│ ║ l.6: set x +//│ ╙── ^ + :ge set x = 0 //│ ╔══[COMPILATION ERROR] Cannot assign to function 'x' -//│ ║ l.6: set x = 0 -//│ ║ ^^^^^ +//│ ║ l.12: set x = 0 +//│ ║ ^^^^^ //│ ╟── Defined here: //│ ║ l.3: fun x = 1 //│ ╙── ^^^^^^^^^ @@ -17,26 +23,26 @@ val x = 1 :ge set x = 0 //│ ╔══[COMPILATION ERROR] Cannot assign to value 'x' -//│ ║ l.18: set x = 0 +//│ ║ l.24: set x = 0 //│ ║ ^^^^^ //│ ╟── Defined here: -//│ ║ l.15: val x = 1 +//│ ║ l.21: val x = 1 //│ ╙── ^^^^^^^^^ :ge mut val x = 1 //│ ╔══[COMPILATION ERROR] Mutable 'val' definitions are only valid as members of a module, object, or class definition -//│ ║ l.28: mut val x = 1 +//│ ║ l.34: mut val x = 1 //│ ╙── ^^^^^^^^^ :ge set x = 0 //│ ╔══[COMPILATION ERROR] Cannot assign to symbol 'x' -//│ ║ l.34: set x = 0 +//│ ║ l.40: set x = 0 //│ ║ ^^^^^ //│ ╟── Defined here: -//│ ║ l.28: mut val x = 1 +//│ ║ l.34: mut val x = 1 //│ ╙── ^^^^^^^^^ @@ -45,10 +51,10 @@ fun foo() = val x = 1 set x = 2 //│ ╔══[COMPILATION ERROR] Cannot assign to value 'x' -//│ ║ l.46: set x = 2 +//│ ║ l.52: set x = 2 //│ ║ ^^^^^ //│ ╟── Defined here: -//│ ║ l.45: val x = 1 +//│ ║ l.51: val x = 1 //│ ╙── ^^^^^^^^^ :ge @@ -56,13 +62,13 @@ fun foo() = mut val x = 1 set x = 2 //│ ╔══[COMPILATION ERROR] Mutable 'val' definitions are only valid as members of a module, object, or class definition -//│ ║ l.56: mut val x = 1 +//│ ║ l.62: mut val x = 1 //│ ╙── ^^^^^^^^^ //│ ╔══[COMPILATION ERROR] Cannot assign to symbol 'x' -//│ ║ l.57: set x = 2 +//│ ║ l.63: set x = 2 //│ ║ ^^^^^ //│ ╟── Defined here: -//│ ║ l.56: mut val x = 1 +//│ ║ l.62: mut val x = 1 //│ ╙── ^^^^^^^^^ @@ -72,19 +78,19 @@ module X :ge set X = 2 //│ ╔══[COMPILATION ERROR] Cannot assign to overloaded class and module 'X' -//│ ║ l.73: set X = 2 +//│ ║ l.79: set X = 2 //│ ║ ^^^^^ //│ ╟── Defined here: -//│ ║ l.69: class X +//│ ║ l.75: class X //│ ║ ^^^^^^^ -//│ ║ l.70: module X +//│ ║ l.76: module X //│ ╙── ^^^^^^^^ :ge set globalThis = 0 //│ ╔══[COMPILATION ERROR] Cannot assign to symbol 'globalThis' -//│ ║ l.85: set globalThis = 0 +//│ ║ l.91: set globalThis = 0 //│ ╙── ^^^^^^^^^^^^^^ diff --git a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls index 0b31abc18a..865eaadc2a 100644 --- a/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls +++ b/hkmc2/shared/src/test/mlscript/basics/BadDefs.mls @@ -76,3 +76,11 @@ Lol //│ ═══[RUNTIME ERROR] This code cannot be run as its compilation yielded an error. +:todo +trait F +//│ ╔══[INTERNAL ERROR] Compiler reached an unsupported state: trait not yet supported +//│ ╟── The compilation result may be incorrect. +//│ ╙── This is a known compiler limitation; if it is a blocker for you, please report it to the maintainers. +//│ /!!!\ Uncaught error: scala.NotImplementedError: an implementation is missing + + diff --git a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls index d3781622fc..a3cacbcaf5 100644 --- a/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls +++ b/hkmc2/shared/src/test/mlscript/flows/BasicFlows.mls @@ -108,7 +108,7 @@ f(1, 2) //│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = 3 -:fixme +:todo f(...a) //│ /!!!\ Uncaught error: scala.NotImplementedError: tuple spread in flow analysis (of class String) From 950d985ef780b60768809514945ea278ede21a0c Mon Sep 17 00:00:00 2001 From: Lionel Parreaux Date: Fri, 5 Jun 2026 12:20:18 +0800 Subject: [PATCH 13/13] Make CI error on warnings again --- build.sbt | 3 --- 1 file changed, 3 deletions(-) diff --git a/build.sbt b/build.sbt index e9ead3a0ba..959da337ce 100644 --- a/build.sbt +++ b/build.sbt @@ -34,9 +34,6 @@ lazy val hkmc2 = crossProject(JSPlatform, JVMPlatform).in(file("hkmc2")) watchSources += WatchSource( baseDirectory.value.getParentFile()/"shared"/"src"/"test"/"diff", "*.mls", NothingFilter), - // TODO remove when codebase becomes production-ready - scalacOptions -= "-Wconf:any:error", - // scalacOptions ++= Seq("-indent", "-rewrite"), scalacOptions ++= Seq("-new-syntax", "-rewrite"), // scalacOptions ++= Seq("-language:experimental.modularity"), // https://docs.scala-lang.org/scala3/reference/experimental/modularity.html