diff --git a/shared/src/main/scala/mlscript/ucs/Clause.scala b/shared/src/main/scala/mlscript/ucs/Clause.scala index 3b3415559..5f0bd38bb 100644 --- a/shared/src/main/scala/mlscript/ucs/Clause.scala +++ b/shared/src/main/scala/mlscript/ucs/Clause.scala @@ -21,24 +21,46 @@ abstract class Clause { * @return */ val locations: Ls[Loc] + + protected final def bindingsToString: String = + (if (bindings.isEmpty) "" else " with " + Clause.showBindings(bindings)) } object Clause { + final case class MatchLiteral( + scrutinee: Scrutinee, + literal: SimpleTerm + )(override val locations: Ls[Loc]) extends Clause { + override def toString(): String = s"«$scrutinee is $literal" + bindingsToString + } + final case class MatchClass( scrutinee: Scrutinee, className: Var, fields: Ls[Str -> Var] - )(override val locations: Ls[Loc]) extends Clause + )(override val locations: Ls[Loc]) extends Clause { + override def toString(): String = s"«$scrutinee is $className»" + bindingsToString + } final case class MatchTuple( scrutinee: Scrutinee, arity: Int, fields: Ls[Str -> Var] - )(override val locations: Ls[Loc]) extends Clause + )(override val locations: Ls[Loc]) extends Clause { + override def toString(): String = s"«$scrutinee is Tuple#$arity»" + bindingsToString + } - final case class BooleanTest(test: Term)(override val locations: Ls[Loc]) extends Clause + final case class BooleanTest(test: Term)( + override val locations: Ls[Loc] + ) extends Clause { + override def toString(): String = s"«$test»" + bindingsToString + } - final case class Binding(name: Var, term: Term)(override val locations: Ls[Loc]) extends Clause + final case class Binding(name: Var, term: Term)( + override val locations: Ls[Loc] + ) extends Clause { + override def toString(): String = s"«$name = $term»" + bindingsToString + } def showBindings(bindings: Ls[(Bool, Var, Term)]): Str = bindings match { @@ -48,20 +70,7 @@ object Clause { }.mkString("(", ", ", ")") } - - def showClauses(clauses: Iterable[Clause]): Str = { - clauses.iterator.map { clause => - (clause match { - case Clause.BooleanTest(test) => s"«$test»" - case Clause.MatchClass(scrutinee, Var(className), fields) => - s"«$scrutinee is $className»" - case Clause.MatchTuple(scrutinee, arity, fields) => - s"«$scrutinee is Tuple#$arity»" - case Clause.Binding(Var(name), term) => - s"«$name = $term»" - }) + (if (clause.bindings.isEmpty) "" else " with " + showBindings(clause.bindings)) - }.mkString("", " and ", "") - } + def showClauses(clauses: Iterable[Clause]): Str = clauses.mkString("", " and ", "") def print(println: (=> Any) => Unit, conjunctions: Iterable[Conjunction -> Term]): Unit = { println("Flattened conjunctions") diff --git a/shared/src/main/scala/mlscript/ucs/Conjunction.scala b/shared/src/main/scala/mlscript/ucs/Conjunction.scala index ff9c6a2fb..a849f42f1 100644 --- a/shared/src/main/scala/mlscript/ucs/Conjunction.scala +++ b/shared/src/main/scala/mlscript/ucs/Conjunction.scala @@ -3,6 +3,7 @@ package mlscript.ucs import mlscript._, utils._, shorthands._ import Clause._, helpers._ import scala.collection.mutable.Buffer +import scala.annotation.tailrec /** * A `Conjunction` represents a list of `Clause`s. @@ -53,13 +54,20 @@ final case class Conjunction(clauses: Ls[Clause], trailingBindings: Ls[(Bool, Va def +(lastBinding: (Bool, Var, Term)): Conjunction = Conjunction(clauses, trailingBindings :+ lastBinding) - def separate(expectedScrutinee: Scrutinee): Opt[(MatchClass, Conjunction)] = { - def rec(past: Ls[Clause], upcoming: Ls[Clause]): Opt[(Ls[Clause], MatchClass, Ls[Clause])] = { + def separate(expectedScrutinee: Scrutinee): Opt[(MatchClass \/ MatchLiteral, Conjunction)] = { + @tailrec + def rec(past: Ls[Clause], upcoming: Ls[Clause]): Opt[(Ls[Clause], MatchClass \/ MatchLiteral, Ls[Clause])] = { upcoming match { case Nil => N + case (head @ MatchLiteral(scrutinee, _)) :: tail => + if (scrutinee === expectedScrutinee) { + S((past, R(head), tail)) + } else { + rec(past :+ head, tail) + } case (head @ MatchClass(scrutinee, _, _)) :: tail => if (scrutinee === expectedScrutinee) { - S((past, head, tail)) + S((past, L(head), tail)) } else { rec(past :+ head, tail) } diff --git a/shared/src/main/scala/mlscript/ucs/Desugarer.scala b/shared/src/main/scala/mlscript/ucs/Desugarer.scala index 5191c13b5..a442be6c0 100644 --- a/shared/src/main/scala/mlscript/ucs/Desugarer.scala +++ b/shared/src/main/scala/mlscript/ucs/Desugarer.scala @@ -99,8 +99,13 @@ class Desugarer extends TypeDefs { self: Typer => def makeScrutinee(term: Term, matchRootLoc: Opt[Loc])(implicit ctx: Ctx): Scrutinee = traceUCS(s"Making a scrutinee for `$term`") { term match { - case _: SimpleTerm => Scrutinee(N, term)(matchRootLoc) - case _ => Scrutinee(S(makeLocalizedName(term)), term)(matchRootLoc) + case _: Var => + printlnUCS(s"The scrutinee does not need an alias.") + Scrutinee(N, term)(matchRootLoc) + case _ => + val localizedName = makeLocalizedName(term) + printlnUCS(s"The scrutinee needs an alias: $localizedName") + Scrutinee(S(localizedName), term)(matchRootLoc) } }() @@ -160,9 +165,13 @@ class Desugarer extends TypeDefs { self: Typer => case Var("_") => Nil // This case handles literals. // x is true | x is false | x is 0 | x is "text" | ... - case literal @ (Var("true") | Var("false") | _: Lit) => - val test = mkBinOp(scrutinee.reference, Var("=="), literal) - val clause = Clause.BooleanTest(test)(scrutinee.term.toLoc.toList ::: literal.toLoc.toList) + case literal: Var if literal.name === "true" || literal.name === "false" => + val clause = Clause.MatchLiteral(scrutinee, literal)(scrutinee.term.toLoc.toList ::: literal.toLoc.toList) + clause.bindings = scrutinee.asBinding.toList + printlnUCS(s"Add bindings to the clause: ${scrutinee.asBinding}") + clause :: Nil + case literal: Lit => + val clause = Clause.MatchLiteral(scrutinee, literal)(scrutinee.term.toLoc.toList ::: literal.toLoc.toList) clause.bindings = scrutinee.asBinding.toList printlnUCS(s"Add bindings to the clause: ${scrutinee.asBinding}") clause :: Nil @@ -515,22 +524,34 @@ class Desugarer extends TypeDefs { self: Typer => */ type ExhaustivenessMap = Map[Str \/ Int, Map[Var, MutCase]] - def getScurtineeKey(scrutinee: Scrutinee)(implicit ctx: Ctx, raise: Raise): Str \/ Int = { - scrutinee.term match { - // The original scrutinee is an reference. - case v @ Var(name) => - ctx.env.get(name) match { - case S(VarSymbol(_, defVar)) => defVar.uid.fold[Str \/ Int](L(v.name))(R(_)) - case S(_) | N => L(v.name) - } - // Otherwise, the scrutinee has a temporary name. - case _ => - scrutinee.local match { - case N => throw new Error("check your `makeScrutinee`") - case S(localNameVar) => L(localNameVar.name) - } - } - } + /** + * This method obtains a proper key of the given scrutinee + * for memorizing patterns belongs to the scrutinee. + * + * @param scrutinee the scrutinee + * @param ctx the context + * @param raise we need this to raise errors. + * @return the variable name or the variable ID + */ + def getScurtineeKey(scrutinee: Scrutinee)(implicit ctx: Ctx, raise: Raise): Str \/ Int = + traceUCS(s"[getScrutineeKey] $scrutinee") { + scrutinee.term match { + // The original scrutinee is an reference. + case v @ Var(name) => + printlnUCS("The original scrutinee is an reference.") + ctx.env.get(name) match { + case S(VarSymbol(_, defVar)) => defVar.uid.fold[Str \/ Int](L(v.name))(R(_)) + case S(_) | N => L(v.name) + } + // Otherwise, the scrutinee was localized because it might be effectful. + case _ => + printlnUCS("The scrutinee was localized because it might be effectful.") + scrutinee.local match { + case N => throw new Error("check your `makeScrutinee`") + case S(localNameVar) => L(localNameVar.name) + } + } + }() /** * Check the exhaustiveness of the given `MutCaseOf`. @@ -542,10 +563,8 @@ class Desugarer extends TypeDefs { self: Typer => def checkExhaustive (t: MutCaseOf, parentOpt: Opt[MutCaseOf]) (implicit scrutineePatternMap: ExhaustivenessMap, ctx: Ctx, raise: Raise) - : Unit = { - printlnUCS(s"Check exhaustiveness of ${t.describe}") - indent += 1 - try t match { + : Unit = traceUCS(s"[checkExhaustive] ${t.describe}") { + t match { case _: Consequent => () case MissingCase => parentOpt match { @@ -567,7 +586,10 @@ class Desugarer extends TypeDefs { self: Typer => case S(_) if default.isDefined => printlnUCS("The match has a default branch. So, it is always safe.") case S(patternMap) => - printlnUCS(s"The exhaustiveness map is ${scrutineePatternMap}") + printlnUCS(s"The exhaustiveness map is") + scrutineePatternMap.foreach { case (key, matches) => + printlnUCS(s"- $key -> ${matches.keysIterator.mkString(", ")}") + } printlnUCS(s"The scrutinee key is ${getScurtineeKey(scrutinee)}") printlnUCS("Pattern map of the scrutinee:") if (patternMap.isEmpty) @@ -575,10 +597,15 @@ class Desugarer extends TypeDefs { self: Typer => else patternMap.foreach { case (key, mutCase) => printlnUCS(s"- $key => $mutCase")} // Filter out missing cases in `branches`. - val missingCases = patternMap.removedAll(branches.iterator.map { - case MutCase(classNameVar -> _, _) => classNameVar + val missingCases = patternMap.removedAll(branches.iterator.flatMap { + case MutCase.Literal(tof @ Var(n), _) if n === "true" || n === "false" => Some(tof) + case MutCase.Literal(_, _) => None + case MutCase.Constructor(classNameVar -> _, _) => Some(classNameVar) }) - printlnUCS(s"Number of missing cases: ${missingCases.size}") + printlnUCS("Missing cases") + missingCases.foreach { case (key, m) => + printlnUCS(s"- $key -> ${m}") + } if (!missingCases.isEmpty) { throw new DesugaringException({ val numMissingCases = missingCases.size @@ -597,18 +624,16 @@ class Desugarer extends TypeDefs { self: Typer => } } default.foreach(checkExhaustive(_, S(t))) - branches.foreach { case MutCase(_, consequent) => - checkExhaustive(consequent, S(t)) + branches.foreach { branch => + checkExhaustive(branch.consequent, S(t)) } - } finally indent -= 1 - } + } + }() - def summarizePatterns(t: MutCaseOf)(implicit ctx: Ctx, raise: Raise): ExhaustivenessMap = { + def summarizePatterns(t: MutCaseOf)(implicit ctx: Ctx, raise: Raise): ExhaustivenessMap = traceUCS("[summarizePatterns]") { val m = MutMap.empty[Str \/ Int, MutMap[Var, MutCase]] - def rec(t: MutCaseOf): Unit = { - printlnUCS(s"Summarize pattern of ${t.describe}") - indent += 1 - try t match { + def rec(t: MutCaseOf): Unit = traceUCS(s"[rec] ${t.describe}") { + t match { case Consequent(term) => () case MissingCase => () case IfThenElse(_, whenTrue, whenFalse) => @@ -616,23 +641,36 @@ class Desugarer extends TypeDefs { self: Typer => rec(whenFalse) case Match(scrutinee, branches, default) => val key = getScurtineeKey(scrutinee) - branches.foreach { mutCase => - val patternMap = m.getOrElseUpdate( key, MutMap.empty) - if (!patternMap.contains(mutCase.patternFields._1)) { - patternMap += ((mutCase.patternFields._1, mutCase)) - } - rec(mutCase.consequent) + val patternMap = m.getOrElseUpdate(key, MutMap.empty) + branches.foreach { + case mutCase @ MutCase.Literal(literal, consequent) => + literal match { + case tof @ Var(n) if n === "true" || n === "false" => + if (!patternMap.contains(tof)) { + patternMap += ((tof, mutCase)) + } + case _ => () // TODO: Summarize literals. + } + rec(consequent) + case mutCase @ MutCase.Constructor((className, _), consequent) => + if (!patternMap.contains(className)) { + patternMap += ((className, mutCase)) + } + rec(consequent) } default.foreach(rec) - } finally indent -= 1 - } + } + }() rec(t) - printlnUCS("Exhaustiveness map") - m.foreach { case (scrutinee, patterns) => - printlnUCS(s"- $scrutinee => " + patterns.keys.mkString(", ")) - } + printlnUCS("Summarized patterns") + if (m.isEmpty) + printlnUCS("") + else + m.foreach { case (scrutinee, patterns) => + printlnUCS(s"- $scrutinee => " + patterns.keysIterator.mkString(", ")) + } Map.from(m.iterator.map { case (key, patternMap) => key -> Map.from(patternMap) }) - } + }() protected def constructTerm(m: MutCaseOf)(implicit ctx: Ctx): Term = { def rec(m: MutCaseOf)(implicit defs: Set[Var]): Term = m match { @@ -640,10 +678,13 @@ class Desugarer extends TypeDefs { self: Typer => case Match(scrutinee, branches, wildcard) => def rec2(xs: Ls[MutCase]): CaseBranches = xs match { - case MutCase(className -> fields, cases) :: next => + case MutCase.Constructor(className -> fields, cases) :: next => // TODO: expand bindings here val consequent = rec(cases)(defs ++ fields.iterator.map(_._2)) Case(className, mkLetFromFields(scrutinee, fields.toList, consequent), rec2(next)) + case MutCase.Literal(literal, cases) :: next => + val consequent = rec(cases) + Case(literal, consequent, rec2(next)) case Nil => wildcard.fold[CaseBranches](NoCases) { rec(_) |> Wildcard } } diff --git a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala index 93e04c789..9281d0794 100644 --- a/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala +++ b/shared/src/main/scala/mlscript/ucs/MutCaseOf.scala @@ -79,12 +79,16 @@ object MutCaseOf { rec(whenFalse, indent + 1, "") case Match(scrutinee, branches, default) => lines += baseIndent + leading + bindingNames + showScrutinee(scrutinee) + " match" - branches.foreach { case MutCase(Var(className) -> fields, consequent) => - lines += s"$baseIndent case $className =>" - fields.foreach { case (field, Var(alias)) => - lines += s"$baseIndent let $alias = .$field" - } - rec(consequent, indent + 2, "") + branches.foreach { + case MutCase.Literal(literal, consequent) => + lines += s"$baseIndent case $literal =>" + rec(consequent, indent + 1, "") + case MutCase.Constructor(Var(className) -> fields, consequent) => + lines += s"$baseIndent case $className =>" + fields.foreach { case (field, Var(alias)) => + lines += s"$baseIndent let $alias = .$field" + } + rec(consequent, indent + 2, "") } default.foreach { consequent => lines += s"$baseIndent default" @@ -100,20 +104,12 @@ object MutCaseOf { lines.toList } - /** - * MutCase is a _mutable_ representation of a case in `MutCaseOf.Match`. - * - * @param patternFields the alias to the fields - * @param consequent the consequential `MutCaseOf` - */ - final case class MutCase( - val patternFields: Var -> Buffer[Str -> Var], - var consequent: MutCaseOf, - ) { - def matches(expected: Var): Bool = matches(expected.name) - def matches(expected: Str): Bool = patternFields._1.name === expected - def addFields(fields: Iterable[Str -> Var]): Unit = - patternFields._2 ++= fields.iterator.filter(!patternFields._2.contains(_)) + sealed abstract class MutCase { + var consequent: MutCaseOf + + def matches(expected: Var): Bool + def matches(expected: Str): Bool + def matches(expected: Lit): Bool // Note 1 // ====== @@ -142,7 +138,38 @@ object MutCaseOf { } } - import Clause.{MatchClass, MatchTuple, BooleanTest, Binding} + object MutCase { + final case class Literal( + val literal: SimpleTerm, + var consequent: MutCaseOf, + ) extends MutCase { + override def matches(expected: Var): Bool = literal match { + case tof @ Var(n) if n === "true" || n === "false" => expected === tof + case _ => false + } + override def matches(expected: Str): Bool = false + override def matches(expected: Lit): Bool = literal === expected + } + + /** + * MutCase is a _mutable_ representation of a case in `MutCaseOf.Match`. + * + * @param patternFields the alias to the fields + * @param consequent the consequential `MutCaseOf` + */ + final case class Constructor( + val patternFields: Var -> Buffer[Str -> Var], + var consequent: MutCaseOf, + ) extends MutCase { + override def matches(expected: Var): Bool = matches(expected.name) + override def matches(expected: Str): Bool = patternFields._1.name === expected + override def matches(expected: Lit): Bool = false + def addFields(fields: Iterable[Str -> Var]): Unit = + patternFields._2 ++= fields.iterator.filter(!patternFields._2.contains(_)) + } + } + + import Clause.{MatchLiteral, MatchClass, MatchTuple, BooleanTest, Binding} // A short-hand for pattern matchings with only true and false branches. final case class IfThenElse(condition: Term, var whenTrue: MutCaseOf, var whenFalse: MutCaseOf) extends MutCaseOf { @@ -223,16 +250,17 @@ object MutCaseOf { case N => val newBranch = buildFirst(Conjunction(tail, trailingBindings), term) newBranch.addBindings(head.bindings) - branches += MutCase(tupleClassName -> Buffer.from(fields), newBranch) + branches += MutCase.Constructor(tupleClassName -> Buffer.from(fields), newBranch) .withLocations(head.locations) // Found existing pattern. - case S(branch) => + case S(branch: MutCase.Constructor) => branch.consequent.addBindings(head.bindings) branch.addFields(fields) branch.consequent.merge(Conjunction(tail, trailingBindings) -> term) } // A wild card case. We should propagate wildcard to every default positions. - case Conjunction(Nil, trailingBindings) -> term => mergeDefault(trailingBindings, term) + case Conjunction(Nil, trailingBindings) -> term => + mergeDefault(trailingBindings, term) // TODO: Handle the int result here. // The conditions to be inserted does not overlap with me. case conjunction -> term => wildcard match { @@ -243,27 +271,44 @@ object MutCaseOf { } } // Found a match condition against the same scrutinee - case S((head @ MatchClass(_, className, fields), remainingConditions)) => + case S(L(head @ MatchClass(_, className, fields)) -> remainingConditions) => branches.find(_.matches(className)) match { // No such pattern. We should create a new one. case N => val newBranch = buildFirst(remainingConditions, branch._2) newBranch.addBindings(head.bindings) - branches += MutCase(className -> Buffer.from(fields), newBranch) + branches += MutCase.Constructor(className -> Buffer.from(fields), newBranch) .withLocations(head.locations) // Found existing pattern. - case S(matchCase) => + case S(matchCase: MutCase.Constructor) => // Merge interleaved bindings. matchCase.consequent.addBindings(head.bindings) matchCase.addFields(fields) matchCase.consequent.merge(remainingConditions -> branch._2) } + case S(R(head @ MatchLiteral(_, literal)) -> remainingConditions) => + branches.find(branch => literal match { + case v: Var => branch.matches(v) + case l: Lit => branch.matches(l) + }) match { + // No such pattern. We should create a new one. + case N => + val newConsequent = buildFirst(remainingConditions, branch._2) + newConsequent.addBindings(head.bindings) + branches += MutCase.Literal(literal, newConsequent) + .withLocations(head.locations) + case S(matchCase: MutCase.Literal) => + // Merge interleaved bindings. + matchCase.consequent.addBindings(head.bindings) + matchCase.consequent.merge(remainingConditions -> branch._2) + } } } def mergeDefault(bindings: Ls[(Bool, Var, Term)], default: Term)(implicit raise: Diagnostic => Unit): Int = { branches.iterator.map { - case MutCase(_, consequent) => consequent.mergeDefault(bindings, default) + case MutCase.Constructor(_, consequent) => consequent.mergeDefault(bindings, default) + case MutCase.Literal(_, consequent) => consequent.mergeDefault(bindings, default) }.sum + { wildcard match { case N => @@ -296,16 +341,21 @@ object MutCaseOf { case Conjunction(head :: tail, trailingBindings) => val realTail = Conjunction(tail, trailingBindings) (head match { + case MatchLiteral(scrutinee, literal) => + val branches = Buffer( + MutCase.Literal(literal, rec(realTail)).withLocation(literal.toLoc) + ) + Match(scrutinee, branches, N) case BooleanTest(test) => IfThenElse(test, rec(realTail), MissingCase) case MatchClass(scrutinee, className, fields) => val branches = Buffer( - MutCase(className -> Buffer.from(fields), rec(realTail)) + MutCase.Constructor(className -> Buffer.from(fields), rec(realTail)) .withLocations(head.locations) ) Match(scrutinee, branches, N) case MatchTuple(scrutinee, arity, fields) => val branches = Buffer( - MutCase(Var(s"Tuple#$arity") -> Buffer.from(fields), rec(realTail)) + MutCase.Constructor(Var(s"Tuple#$arity") -> Buffer.from(fields), rec(realTail)) .withLocations(head.locations) ) Match(scrutinee, branches, N) diff --git a/shared/src/test/diff/codegen/Mixin.mls b/shared/src/test/diff/codegen/Mixin.mls index 16a0b6d2c..b10c745fb 100644 --- a/shared/src/test/diff/codegen/Mixin.mls +++ b/shared/src/test/diff/codegen/Mixin.mls @@ -373,7 +373,7 @@ fun mk(n) = if n is 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) TestLang.eval(mk(0)) -//│ fun mk: forall 'E. number -> 'E +//│ fun mk: forall 'E. anything -> 'E //│ int //│ where //│ 'E :> Add['E] | Lit | Neg['E] @@ -381,7 +381,8 @@ TestLang.eval(mk(0)) //│ let typing_unit6 = { cache: {} }; //│ // Query 1 //│ globalThis.mk = function mk(n) { -//│ return n == 0 === true ? Lit(0) : n == 1 === true ? Neg(mk(n)) : Add(mk(n), mk(n)); +//│ let a; +//│ return a = n, a === 0 ? Lit(0) : a === 1 ? Neg(mk(n)) : Add(mk(n), mk(n)); //│ }; //│ // Query 2 //│ res = TestLang.eval(mk(0)); @@ -396,7 +397,8 @@ TestLang.eval(mk(0)) //│ │ ├── Prelude: //│ │ ├── Code: //│ │ ├── globalThis.mk = function mk(n) { -//│ │ ├── return n == 0 === true ? Lit(0) : n == 1 === true ? Neg(mk(n)) : Add(mk(n), mk(n)); +//│ │ ├── let a; +//│ │ ├── return a = n, a === 0 ? Lit(0) : a === 1 ? Neg(mk(n)) : Add(mk(n), mk(n)); //│ │ ├── }; //│ │ ├── Intermediate: [Function: mk] //│ │ └── Reply: [success] [Function: mk] @@ -417,7 +419,7 @@ class Foo(x: int) :e class Bar(x: int, y: int) extends Foo(x + y) //│ ╔══[ERROR] Class inheritance is not supported yet (use mixins) -//│ ║ l.418: class Bar(x: int, y: int) extends Foo(x + y) +//│ ║ l.420: class Bar(x: int, y: int) extends Foo(x + y) //│ ╙── ^^^^^^^^^^ //│ class Bar(x: int, y: int) @@ -530,7 +532,7 @@ mixin Base { fun x = y } //│ ╔══[ERROR] identifier not found: y -//│ ║ l.530: fun x = y +//│ ║ l.532: fun x = y //│ ╙── ^ //│ mixin Base() { //│ fun x: error diff --git a/shared/src/test/diff/ecoop23/ExpressionProblem.mls b/shared/src/test/diff/ecoop23/ExpressionProblem.mls index 2584f3070..659cb988d 100644 --- a/shared/src/test/diff/ecoop23/ExpressionProblem.mls +++ b/shared/src/test/diff/ecoop23/ExpressionProblem.mls @@ -201,7 +201,7 @@ fun mk(n) = if n is 0 then Lit(0) 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) -//│ fun mk: forall 'E. number -> 'E +//│ fun mk: forall 'E. anything -> 'E //│ where //│ 'E :> Add['E] | Lit | Neg['E] diff --git a/shared/src/test/diff/ecoop23/SimpleRegionDSL.mls b/shared/src/test/diff/ecoop23/SimpleRegionDSL.mls index eaad25f32..8e9f46259 100644 --- a/shared/src/test/diff/ecoop23/SimpleRegionDSL.mls +++ b/shared/src/test/diff/ecoop23/SimpleRegionDSL.mls @@ -45,7 +45,7 @@ fun go(x, offset) = else let shared = go(x - 1, round(offset / 2)) Union(Translate(Vector(0 - offset, 0), shared), Translate(Vector(offset, 0), shared)) -//│ fun go: forall 'Region. (int, int,) -> 'Region +//│ fun go: forall 'Region. (0 | int & ~0, int,) -> 'Region //│ where //│ 'Region :> Circle | Union[Translate['Region]] @@ -116,7 +116,7 @@ TestSize.size(Scale(Vector(1, 1), circles)) fun pow(x, a) = if a is 0 then 1 else x * pow(x, a - 1) -//│ fun pow: (int, int,) -> int +//│ fun pow: (int, 0 | int & ~0,) -> int mixin Contains { fun contains(a, p) = @@ -327,7 +327,7 @@ module TestElim extends Eliminate TestElim.eliminate(Outside(Outside(Univ()))) //│ 'a //│ where -//│ 'a :> Univ | Outside['a] | Union['a] | Intersect['a] | Translate['a] | Scale['a] +//│ 'a :> Scale['a] | Univ | Outside['a] | Union['a] | Intersect['a] | Translate['a] //│ res //│ = Univ {} @@ -344,7 +344,7 @@ fun mk(n) = if n is 3 then Intersect(mk(n), mk(n)) 4 then Translate(Vector(0, 0), mk(n)) _ then Scale(Vector(0, 0), mk(n)) -//│ fun mk: forall 'Region. number -> 'Region +//│ fun mk: forall 'Region. anything -> 'Region //│ where //│ 'Region :> Intersect['Region] | Outside['Region] | Scale['Region] | Translate['Region] | Union['Region] @@ -376,7 +376,7 @@ module Lang extends SizeBase, SizeExt, Contains, Text, IsUniv, IsEmpty, Eliminat //│ 'd <: Intersect['d] | Outside['e] | Scale['d] | Translate['d] | Union['d] | Univ | ~Intersect[anything] & ~Outside[anything] & ~Scale[anything] & ~Translate[anything] & ~Union[anything] & ~Univ //│ 'e <: Intersect['e] | Outside['d] | Scale['e] | Translate['e] | Union['e] | Univ | ~Intersect[anything] & ~Outside[anything] & ~Scale[anything] & ~Translate[anything] & ~Union[anything] & ~Univ //│ 'b <: Intersect['b] | Outside['b & (Outside['b] | ~#Outside)] | Scale['b] | Translate['b] | Union['b] | 'c & ~#Intersect & ~#Outside & ~#Scale & ~#Translate & ~#Union -//│ 'c :> Translate['c] | Scale['c] | Outside['c] | Union['c] | Intersect['c] +//│ 'c :> Outside['c] | Union['c] | Intersect['c] | Translate['c] | Scale['c] //│ 'a <: Circle | Intersect['a] | Outside['a] | Translate['a] | Union['a] // TODO investigate diff --git a/shared/src/test/diff/nu/EvalNegNeg.mls b/shared/src/test/diff/nu/EvalNegNeg.mls index b1aef73e5..94db7f6f6 100644 --- a/shared/src/test/diff/nu/EvalNegNeg.mls +++ b/shared/src/test/diff/nu/EvalNegNeg.mls @@ -61,7 +61,7 @@ fun mk(n) = if n is 1 then Neg(mk(n)) _ then Add(mk(n), mk(n)) TestLang.eval(mk(0)) -//│ fun mk: forall 'E. number -> 'E +//│ fun mk: forall 'E. anything -> 'E //│ int //│ where //│ 'E :> Add['E] | Lit | Neg['E] diff --git a/shared/src/test/diff/nu/FilterMap.mls b/shared/src/test/diff/nu/FilterMap.mls index 8c5cd4dd2..4285f53f8 100644 --- a/shared/src/test/diff/nu/FilterMap.mls +++ b/shared/src/test/diff/nu/FilterMap.mls @@ -31,27 +31,9 @@ fun filtermap(f, xs) = if xs is //│ ╔══[ERROR] identifier not found: ys //│ ║ l.27: Cons(y, ys) and f(ys) is //│ ╙── ^^ -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.27: Cons(y, ys) and f(ys) is -//│ ║ ^^^^^^^^ -//│ ║ l.28: false then filtermap(f, ys) -//│ ║ ^^^^^^^^^ -//│ ╟── reference of type `false` is not an instance of type `number` -//│ ║ l.28: false then filtermap(f, ys) -//│ ╙── ^^^^^ -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.27: Cons(y, ys) and f(ys) is -//│ ║ ^^^^^^^^ -//│ ║ l.28: false then filtermap(f, ys) -//│ ║ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -//│ ║ l.29: true then Cons(y, filtermap(f, ys)) -//│ ║ ^^^^^^^^ -//│ ╟── reference of type `true` is not an instance of type `number` -//│ ║ l.29: true then Cons(y, filtermap(f, ys)) -//│ ╙── ^^^^ //│ ╔══[ERROR] type identifier not found: Tuple#2 //│ ╙── -//│ fun filtermap: ((Cons[nothing] | error | Nil) -> number & (Cons[nothing] | Nil) -> error, Cons[anything] | Nil,) -> (Cons[nothing] | Nil | error) +//│ fun filtermap: ((Cons[nothing] | error | Nil) -> anything & (Cons[nothing] | Nil) -> (error | false | true), Cons[anything] | Nil,) -> (Cons[nothing] | Nil | error) //│ Code generation encountered an error: //│ unknown match case: Tuple#2 @@ -70,6 +52,6 @@ fun filtermap(f, xs) = if xs is True then filtermap(f, ys) False then Cons(y, filtermap(f, ys)) Pair(True, z) then Cons(z, filtermap(f, ys)) -//│ fun filtermap: forall 'head 'A. ('head -> (False | Pair[anything, 'A] | True), Cons['head & 'A] | Nil,) -> (Cons['A] | Nil) +//│ fun filtermap: forall 'A 'head. ('head -> (False | Pair[anything, 'A] | True), Cons['head & 'A] | Nil,) -> (Cons['A] | Nil) diff --git a/shared/src/test/diff/ucs/Humiliation.mls b/shared/src/test/diff/ucs/Humiliation.mls index bb55ab0a1..7629b41a9 100644 --- a/shared/src/test/diff/ucs/Humiliation.mls +++ b/shared/src/test/diff/ucs/Humiliation.mls @@ -11,7 +11,7 @@ if 1 is 1 then 1 else 0 //│ = 1 fun test(x) = if x is 1 then 0 else 1 -//│ test: number -> (0 | 1) +//│ test: anything -> (0 | 1) //│ = [Function: test] // It should report duplicated branches. @@ -47,7 +47,7 @@ fun f(x) = Pair(1, 1) then "ones" Pair(y, 1) then x _ then "nah" -//│ f: (Pair & {fst: number, snd: number} & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) +//│ f: (Pair & 'a | ~Pair) -> ("nah" | "ones" | "zeros" | 'a) //│ = [Function: f] class Z() diff --git a/shared/src/test/diff/ucs/LitUCS.mls b/shared/src/test/diff/ucs/LitUCS.mls index f6f4760a4..a9fd1c900 100644 --- a/shared/src/test/diff/ucs/LitUCS.mls +++ b/shared/src/test/diff/ucs/LitUCS.mls @@ -4,37 +4,11 @@ module A //│ module A() -// FIXME // This one is easy to fix but what about the next one? // The following example can better reveal the essence of the problem. fun test(x: 0 | A) = if x is 0 then 0 A then A -//│ ╔══[ERROR] Type mismatch in application: -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ║ ^ -//│ ╟── type `A` is not an instance of type `number` -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ║ ^ -//│ ╟── but it flows into reference with expected type `number` -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ╙── ^ -//│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ║ ^^^^ -//│ ║ l.11: 0 then 0 -//│ ║ ^^^^^^^^^^ -//│ ║ l.12: A then A -//│ ║ ^^^^^^^^^^ -//│ ╟── type `0` is not an instance of type `A` -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ║ ^ -//│ ╟── but it flows into reference with expected type `A` -//│ ║ l.10: fun test(x: 0 | A) = if x is -//│ ║ ^ -//│ ╟── Note: constraint arises from class pattern: -//│ ║ l.12: A then A -//│ ╙── ^ //│ fun test: (x: 0 | A,) -> (0 | A) // FIXME @@ -43,24 +17,24 @@ fun test(x: 0 | A) = x == 0 then 0 x is A then A //│ ╔══[ERROR] Type mismatch in operator application: -//│ ║ l.43: x == 0 then 0 +//│ ║ l.17: x == 0 then 0 //│ ║ ^^^^ //│ ╟── type `A` is not an instance of type `number` -//│ ║ l.41: fun test(x: 0 | A) = +//│ ║ l.15: fun test(x: 0 | A) = //│ ║ ^ //│ ╟── but it flows into reference with expected type `number` -//│ ║ l.43: x == 0 then 0 +//│ ║ l.17: x == 0 then 0 //│ ╙── ^ //│ ╔══[ERROR] Type mismatch in `case` expression: -//│ ║ l.44: x is A then A +//│ ║ l.18: x is A then A //│ ║ ^^^^^^^^^^^^^ //│ ╟── type `0` is not an instance of type `A` -//│ ║ l.41: fun test(x: 0 | A) = +//│ ║ l.15: fun test(x: 0 | A) = //│ ║ ^ //│ ╟── but it flows into reference with expected type `A` -//│ ║ l.44: x is A then A +//│ ║ l.18: x is A then A //│ ║ ^ //│ ╟── Note: constraint arises from class pattern: -//│ ║ l.44: x is A then A +//│ ║ l.18: x is A then A //│ ╙── ^ //│ fun test: (x: 0 | A,) -> (0 | A) diff --git a/shared/src/test/diff/ucs/WeirdIf.mls b/shared/src/test/diff/ucs/WeirdIf.mls index 708280aa3..4fe9061ee 100644 --- a/shared/src/test/diff/ucs/WeirdIf.mls +++ b/shared/src/test/diff/ucs/WeirdIf.mls @@ -79,20 +79,17 @@ fun f(x) = //│ f: anything -> "bruh" //│ = [Function: f3] -:e -:ge // Hmmmmmm, this one is valid but how to get it work? fun boolToStr(x) = if x is true then "yah" false then "nah" -//│ ╔══[ERROR] The case when this is false is not handled: == (x,) (false,) -//│ ║ l.86: if x is -//│ ║ ^^^^ -//│ ║ l.87: true then "yah" -//│ ║ ^^^^^^^^^^^^^^^^^^^ -//│ ║ l.88: false then "nah" -//│ ╙── ^^^^^^^^^ -//│ boolToStr: anything -> error -//│ Code generation encountered an error: -//│ if expression was not desugared +//│ boolToStr: bool -> ("nah" | "yah") +//│ = [Function: boolToStr] + +boolToStr of true +boolToStr of false +//│ res: "nah" | "yah" +//│ = 'yah' +//│ res: "nah" | "yah" +//│ = 'nah'